diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..b881a01d --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @cloudinary/video diff --git a/.github/workflows/release-edge.yml b/.github/workflows/release-edge.yml index 113ab2e2..cfebfb56 100644 --- a/.github/workflows/release-edge.yml +++ b/.github/workflows/release-edge.yml @@ -4,6 +4,7 @@ on: push: branches: - 'edge' + workflow_dispatch: jobs: release: @@ -30,6 +31,7 @@ jobs: uses: google-github-actions/release-please-action@v3 id: release with: + token: ${{ secrets.BOT_TOKEN }} release-type: node release-as: ${{ steps.calculate-edge-version.outputs.next-edge }} default-branch: ${{ github.ref_name }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1fd63f6c..41db39b0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,6 +14,7 @@ jobs: uses: google-github-actions/release-please-action@v3 id: release with: + token: ${{ secrets.BOT_TOKEN }} release-type: node default-branch: ${{ github.ref_name }} diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 11734ebf..39f9aa25 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -20,6 +20,9 @@ jobs: - name: Install dependencies run: npm ci + - name: Build + run: npm run build-all + - name: Unit tests run: npm run test:unit diff --git a/CHANGELOG.md b/CHANGELOG.md index e39a52a4..1a354c00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -596,4 +596,4 @@ Other changes * Set seekThumbnails to false by default * Generate https sources by default * Move expose-loader to dev dependencies -* Update dependencies +* Update dependencies \ No newline at end of file diff --git a/babel.config.js b/babel.config.js index 8d3706cf..c2c621b3 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,5 +1,14 @@ module.exports = { - presets: ['@babel/preset-env'], + presets: [ + [ + '@babel/preset-env', + { + targets: { + browsers: ['>0.25%', 'not ie 11', 'not op_mini all'] + } + } + ] + ], env: { test: { plugins: ['@babel/plugin-transform-runtime'] diff --git a/docs/360.html b/docs/360.html index 348092ef..a0a7e0fb 100644 --- a/docs/360.html +++ b/docs/360.html @@ -10,8 +10,8 @@ We're loading scripts & style dynamically for development/testing. Real-world usage would look like this: - - + + --> diff --git a/docs/_template.html b/docs/_template.html index 7d3533ea..af95fd85 100644 --- a/docs/_template.html +++ b/docs/_template.html @@ -17,8 +17,8 @@ We're loading scripts & style dynamically for development/testing. Real-world usage would look like this: - - + + --> diff --git a/docs/adaptive-streaming.html b/docs/adaptive-streaming.html index 32cada44..487d32d4 100644 --- a/docs/adaptive-streaming.html +++ b/docs/adaptive-streaming.html @@ -19,8 +19,8 @@ We're loading scripts & style dynamically for development/testing. Real-world usage would look like this: - - + + diff --git a/docs/analytics.html b/docs/analytics.html index bce11f55..d4d51c99 100644 --- a/docs/analytics.html +++ b/docs/analytics.html @@ -17,8 +17,8 @@ We're loading scripts & style dynamically for development/testing. Real-world usage would look like this: - - + + --> diff --git a/docs/api.html b/docs/api.html index bd59b22a..3bf6bc8f 100644 --- a/docs/api.html +++ b/docs/api.html @@ -17,8 +17,8 @@ We're loading scripts & style dynamically for development/testing. Real-world usage would look like this: - - + + --> diff --git a/docs/audio.html b/docs/audio.html index 058fb2a7..d59fe58a 100644 --- a/docs/audio.html +++ b/docs/audio.html @@ -17,8 +17,8 @@ We're loading scripts & style dynamically for development/testing. Real-world usage would look like this: - - + + --> diff --git a/docs/autoplay-fallback.html b/docs/autoplay-fallback.html index 8c1e6928..0ae89504 100644 --- a/docs/autoplay-fallback.html +++ b/docs/autoplay-fallback.html @@ -17,8 +17,8 @@ We're loading scripts & style dynamically for development/testing. Real-world usage would look like this: - - + + --> diff --git a/docs/autoplay-on-scroll.html b/docs/autoplay-on-scroll.html index f92fb99b..ca115618 100644 --- a/docs/autoplay-on-scroll.html +++ b/docs/autoplay-on-scroll.html @@ -17,8 +17,8 @@ We're loading scripts & style dynamically for development/testing. Real-world usage would look like this: - - + + --> diff --git a/docs/chapters.html b/docs/chapters.html index f715978e..5fba7bfc 100644 --- a/docs/chapters.html +++ b/docs/chapters.html @@ -17,8 +17,8 @@ We're loading scripts & style dynamically for development/testing. Real-world usage would look like this: - - + + --> @@ -33,14 +33,12 @@ chapters: { url: 'https://res.cloudinary.com/tsachi/raw/upload/tests/snow_horses_chapters_k1e1ff.vtt' }, + chaptersButton: true }); const playerSourceConf = cloudinary.videoPlayer('player-config', { cloud_name: 'demo', - controlBar: { - pictureInPictureToggle: false, - chaptersButton: true - }, + chaptersButton: true }); playerSourceConf.source('snow_horses', { @@ -150,14 +148,12 @@

Example Code:

chapters: { url: 'https://res.cloudinary.com/tsachi/raw/upload/tests/snow_horses_chapters_k1e1ff.vtt' }, + chaptersButton: true }); const playerSourceConf = cloudinary.videoPlayer('player-config', { cloud_name: 'demo', - controlBar: { - pictureInPictureToggle: false, - chaptersButton: true - }, + chaptersButton: true }); playerSourceConf.source('snow_horses', { diff --git a/docs/cloudinary-analytics-multiple-videos.html b/docs/cloudinary-analytics-multiple-videos.html index 4a480c67..8186e416 100644 --- a/docs/cloudinary-analytics-multiple-videos.html +++ b/docs/cloudinary-analytics-multiple-videos.html @@ -17,8 +17,8 @@ We're loading scripts & style dynamically for development/testing. Real-world usage would look like this: - - + + --> diff --git a/docs/cloudinary-analytics.html b/docs/cloudinary-analytics.html index 68d92ae9..f464cd20 100644 --- a/docs/cloudinary-analytics.html +++ b/docs/cloudinary-analytics.html @@ -17,8 +17,8 @@ We're loading scripts & style dynamically for development/testing. Real-world usage would look like this: - - + + --> diff --git a/docs/codec-fallback.html b/docs/codec-fallback.html index db750b9d..26367a47 100644 --- a/docs/codec-fallback.html +++ b/docs/codec-fallback.html @@ -17,8 +17,8 @@ We're loading scripts & style dynamically for development/testing. Real-world usage would look like this: - - + + --> diff --git a/docs/colors.html b/docs/colors.html index dadd2a1a..c235b6b5 100644 --- a/docs/colors.html +++ b/docs/colors.html @@ -17,8 +17,8 @@ We're loading scripts & style dynamically for development/testing. Real-world usage would look like this: - - + + --> diff --git a/docs/components.html b/docs/components.html index be36befa..a681d699 100644 --- a/docs/components.html +++ b/docs/components.html @@ -17,8 +17,8 @@ We're loading scripts & style dynamically for development/testing. Real-world usage would look like this: - - + + --> diff --git a/docs/custom-cld-errors.html b/docs/custom-cld-errors.html index 9ce7714d..bb5e6613 100644 --- a/docs/custom-cld-errors.html +++ b/docs/custom-cld-errors.html @@ -17,8 +17,8 @@ We're loading scripts & style dynamically for development/testing. Real-world usage would look like this: - - + + --> diff --git a/docs/es-imports.html b/docs/es-imports.html index b674cac7..4366425d 100644 --- a/docs/es-imports.html +++ b/docs/es-imports.html @@ -17,8 +17,8 @@ We're loading scripts & style dynamically for development/testing. Real-world usage would look like this: - - + + --> diff --git a/docs/floating-player.html b/docs/floating-player.html index 4ca0b2a2..a488ee51 100644 --- a/docs/floating-player.html +++ b/docs/floating-player.html @@ -17,8 +17,8 @@ We're loading scripts & style dynamically for development/testing. Real-world usage would look like this: - - + + --> diff --git a/docs/fluid.html b/docs/fluid.html index ebd7dee3..bec20252 100644 --- a/docs/fluid.html +++ b/docs/fluid.html @@ -17,8 +17,8 @@ We're loading scripts & style dynamically for development/testing. Real-world usage would look like this: - - + + --> diff --git a/docs/force-hls-subtitles-ios.html b/docs/force-hls-subtitles-ios.html index 0c99c78c..250ab640 100644 --- a/docs/force-hls-subtitles-ios.html +++ b/docs/force-hls-subtitles-ios.html @@ -19,8 +19,8 @@ We're loading scripts & style dynamically for development/testing. Real-world usage would look like this: - - + + --> diff --git a/docs/highlights-graph.html b/docs/highlights-graph.html index db15af00..bf6fd357 100644 --- a/docs/highlights-graph.html +++ b/docs/highlights-graph.html @@ -17,8 +17,8 @@ We're loading scripts & style dynamically for development/testing. Real-world usage would look like this: - - + + --> diff --git a/docs/index.html b/docs/index.html index 61e4bfa5..7c48da36 100644 --- a/docs/index.html +++ b/docs/index.html @@ -12,8 +12,8 @@ We're loading scripts & style dynamically for development/testing. Real-world usage would look like this: - - + + --> @@ -49,7 +49,7 @@

Some code examples:

  • API and Events
  • ES Module Imports
  • Playlist
  • -
  • Playlist by Tag
  • +
  • Playlist by Tag
  • Autoplay on Scroll
  • Multiple Players
  • Adaptive Streaming
  • @@ -78,6 +78,7 @@

    Some code examples:

  • Seek Thumbnails
  • Interaction Area
  • Chapters
  • +
  • Profiles
  • diff --git a/docs/live-customer.html b/docs/live-customer.html index f460032d..6af8fa44 100644 --- a/docs/live-customer.html +++ b/docs/live-customer.html @@ -18,8 +18,8 @@ We're loading scripts & style dynamically for development/testing. Real-world usage would look like this: - - + + --> diff --git a/docs/multiple-players.html b/docs/multiple-players.html index 8a3977df..17dd439e 100644 --- a/docs/multiple-players.html +++ b/docs/multiple-players.html @@ -17,8 +17,8 @@ We're loading scripts & style dynamically for development/testing. Real-world usage would look like this: - - + + --> diff --git a/docs/playlist-by-tag-cap.html b/docs/playlist-by-tag-captions.html similarity index 76% rename from docs/playlist-by-tag-cap.html rename to docs/playlist-by-tag-captions.html index c9768dae..4c44e2ec 100644 --- a/docs/playlist-by-tag-cap.html +++ b/docs/playlist-by-tag-captions.html @@ -17,8 +17,8 @@ We're loading scripts & style dynamically for development/testing. Real-world usage would look like this: - - + + --> @@ -27,27 +27,27 @@ - - - - - - - - - - -
    - -

    Cloudinary Video Player

    -

    Playlist by tag

    - - - -
    - -

    - Playlist by tag documentation -

    - -

    Example Code:

    - -
    
    -
    -      <video
    -        id="player"
    -        controls
    -        autoplay
    -        muted
    -        class="cld-video-player"
    -        width="500">
    -      </video>
    -
    -      
    -      
    -
    -        var player = cloudinary.videoPlayer('player', { cloud_name: 'demo' })
    -
    -        var sorter = function(a, b) {
    -          if (a.publicId < b.publicId) return 1;
    -          if (a.publicId > b.publicId) return -1;
    -          return 0;
    -        };
    -
    -        // Fetch playlist by tag. Since this operation involves an API call
    -        // the function returns a Promise when the operation completes.
    -        // The return value is 'player'.
    -        player.playlistByTag('video_race', {
    -          sorter: sorter,
    -          autoAdvance: true,
    -          repeat: true,
    -          presentUpcoming: false
    -        }).then(function() {
    -          var divElem = document.querySelector("#playlist-data");
    -          var list = player.playlist().list().map(function(source) {
    -            return source.publicId()
    -          }).join(', ');
    -
    -          divElem.innerText = "Playlist: " + list
    -        });
    -
    -      
    -    
    -
    - - - diff --git a/docs/playlist.html b/docs/playlist.html index 7784385a..9d07381c 100644 --- a/docs/playlist.html +++ b/docs/playlist.html @@ -17,8 +17,8 @@ We're loading scripts & style dynamically for development/testing. Real-world usage would look like this: - - + + --> @@ -30,7 +30,14 @@ var source2 = { publicId: 'dirt_bike', info: { title: 'Dirt Bike', subtitle: 'A short video of dirt bikes' } }; var source3 = { publicId: 'marmots', info: { title: 'Marmots' } }; - var player = cloudinary.videoPlayer('player',{ + var playlistSources = [source1, source2, source3]; + var playlistOptions = { + autoAdvance: true, + repeat: true, + presentUpcoming: 8 + }; + + var playerHorizontal = cloudinary.videoPlayer('player-horizontal',{ cloud_name: 'demo', playlistWidget: { direction: "horizontal", @@ -38,24 +45,23 @@ } }); - var playlistSources = [source1, source2, source3]; - var playlistOptions = { - autoAdvance: true, - repeat: true, - presentUpcoming: 8 - }; // Auto advance to next video after 0 seconds, repeat the playlist when final video ends, and present upcoming video 8 seconds before the current video ends. - player.playlist(playlistSources, playlistOptions); + playerHorizontal.playlist(playlistSources, playlistOptions); + + var playerVertical = cloudinary.videoPlayer('player-vertical',{ + cloud_name: 'demo', + playlistWidget: { + direction: "vertical", + total: 4 + } + }); + + // Auto advance to next video after 0 seconds, repeat the playlist when final video ends, and present upcoming video 8 seconds before the current video ends. + playerVertical.playlist(playlistSources, playlistOptions); }, false); -
    @@ -64,17 +70,29 @@

    Cloudinary Video Player

    +

    Playlist

    + class="cld-video-player cld-fluid" + > +
    + +
    + +
    +

    diff --git a/docs/poster.html b/docs/poster.html index e811d58c..c390b3bc 100644 --- a/docs/poster.html +++ b/docs/poster.html @@ -19,10 +19,10 @@ We're loading scripts & style dynamically for development/testing. Real-world usage would look like this: - + - + --> diff --git a/docs/profiles.html b/docs/profiles.html new file mode 100644 index 00000000..c3b531ca --- /dev/null +++ b/docs/profiles.html @@ -0,0 +1,118 @@ + + + + + Cloudinary Video Player + + + + + + + + + + + + + + + + + +

    + +

    Cloudinary Video Player

    +

    Profiles

    + +
    Player with default profile
    + + +

    +
    Player with custom profile
    + + +

    Example Code:

    + +
    
    +
    +      <video
    +        id="player-default-profile"
    +        controls
    +        autoplay
    +        muted
    +        class="cld-video-player"
    +        width="500">
    +      </video>
    +
    +    <video
    +        id="player-custom-profile"
    +        controls
    +        autoplay
    +        muted
    +        class="cld-video-player"
    +        width="500">
    +      </video>
    +
    +      
    +      
    +        window.addEventListener('load', async function() {
    +          const playerWithDefaultProfile = await cloudinary.videoPlayerWithProfile('player-default-profile', {
    +            cloud_name: 'demo',
    +            profile: 'cldDefault',
    +          });
    +
    +          playerWithDefaultProfile.source('sea_turtle');
    +
    +          const playerWithCustomProfile = await cloudinary.videoPlayerWithProfile('player-custom-profile', {
    +            cloud_name: 'dwaq5xqm4',
    +            profile: 'https://res.cloudinary.com/dwaq5xqm4/raw/upload/v1/profiles/default_profile.json',
    +          });
    +
    +          playerWithCustomProfile.source('samples/cld-sample-video');
    +        }, false);
    +      
    +    
    +
    + + + diff --git a/docs/raw-url.html b/docs/raw-url.html index 2e97b9f2..43dcaa5d 100644 --- a/docs/raw-url.html +++ b/docs/raw-url.html @@ -17,8 +17,8 @@ We're loading scripts & style dynamically for development/testing. Real-world usage would look like this: - - + + --> @@ -35,7 +35,7 @@ player = cloudinary.videoPlayer('player', config); - player.source('https://res.cloudinary.com/demo/video/upload/sea_turtle.mp4'); + player.source('https://sample-videos.com/video321/mp4/720/big_buck_bunny_720p_10mb.mp4'); adpPlayer = cloudinary.videoPlayer('adpPlayer',config); diff --git a/docs/recommendations.html b/docs/recommendations.html index b9122463..d59475c1 100644 --- a/docs/recommendations.html +++ b/docs/recommendations.html @@ -17,8 +17,8 @@ We're loading scripts & style dynamically for development/testing. Real-world usage would look like this: - - + + --> diff --git a/docs/scripts.js b/docs/scripts.js index b1988219..8316c6b5 100644 --- a/docs/scripts.js +++ b/docs/scripts.js @@ -31,14 +31,25 @@ var isLocal = window.location.hostname === 'localhost' || window.location.hostna // true if testing in an IP page URL var isIpAddress = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(window.location.hostname); +var isNetlify = window.location.hostname.includes('cld-video-player.netlify.app'); + var cdnPrefix = function (source, ver) { - var external = source.startsWith('http'); + var external = source && source.startsWith('http'); + var previewBuild = ver && ver.startsWith('http'); + + if (previewBuild) { + return ver; + } if (!ver && (external || isLocal || isIpAddress)) { return ''; } - return 'https://unpkg.com/cloudinary-video-player@' + (ver || 'edge') + '/dist'; + if (!ver && isNetlify) { + return '../dist'; + } + + return 'https://cdn.jsdelivr.net/npm/cloudinary-video-player@' + (ver || 'edge') + '/dist'; }; // Get scripts & styles from: diff --git a/docs/seek-thumbs.html b/docs/seek-thumbs.html index 0e0c5edb..f378618f 100644 --- a/docs/seek-thumbs.html +++ b/docs/seek-thumbs.html @@ -17,8 +17,8 @@ We're loading scripts & style dynamically for development/testing. Real-world usage would look like this: - - + + --> diff --git a/docs/shoppable.html b/docs/shoppable.html index d6df13ad..952ea55b 100644 --- a/docs/shoppable.html +++ b/docs/shoppable.html @@ -19,8 +19,8 @@ We're loading scripts & style dynamically for development/testing. Real-world usage would look like this: - - + + --> diff --git a/docs/subtitles-and-captions.html b/docs/subtitles-and-captions.html index ce246b82..258cb2bb 100644 --- a/docs/subtitles-and-captions.html +++ b/docs/subtitles-and-captions.html @@ -17,8 +17,8 @@ We're loading scripts & style dynamically for development/testing. Real-world usage would look like this: - - + + --> diff --git a/docs/transformations.html b/docs/transformations.html index 300b753a..94cedfcb 100644 --- a/docs/transformations.html +++ b/docs/transformations.html @@ -17,8 +17,8 @@ We're loading scripts & style dynamically for development/testing. Real-world usage would look like this: - - + + --> diff --git a/docs/ui-config.html b/docs/ui-config.html index 83018332..87cf67d4 100644 --- a/docs/ui-config.html +++ b/docs/ui-config.html @@ -17,8 +17,8 @@ We're loading scripts & style dynamically for development/testing. Real-world usage would look like this: - - + + --> @@ -36,6 +36,7 @@ logoOnclickUrl: 'https://google.com', showLogo: true, showJumpControls: true, + pictureInPictureToggle: true, fontFace: 'Lobster', bigPlayButton: false, playbackRates: [0.5, 1, 1.5, 2] @@ -61,6 +62,7 @@

    Display Configurations

  • Display 10-sec jump buttons
  • Hide big-play-button
  • Show playback rates control
  • +
  • Show PictureInPicture Toggle
  • @@ -92,7 +94,6 @@

    Example Code:

    </video> - var player = cloudinary.videoPlayer('player', { cloud_name: 'demo', publicId: 'snow_horses', @@ -102,6 +103,10 @@

    Example Code:

    logoOnclickUrl: 'https://google.com', showLogo: true, showJumpControls: true, + pictureInPictureToggle: true, + fontFace: 'Lobster', + bigPlayButton: false, + playbackRates: [0.5, 1, 1.5, 2] });
    diff --git a/docs/vast-vpaid.html b/docs/vast-vpaid.html index 943651b0..6ff794cf 100644 --- a/docs/vast-vpaid.html +++ b/docs/vast-vpaid.html @@ -17,8 +17,8 @@ We're loading scripts & style dynamically for development/testing. Real-world usage would look like this: - - + + --> @@ -30,7 +30,7 @@ var adTagUrl = "https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/124319096/external/ad_rule_samples&ciu_szs=300x250&ad_rule=1&impl=s&gdfp_req=1&env=vp&output=vmap&unviewed_position_start=1&cust_params=deployment%3Ddevsite%26sample_ar%3Dpreonly&cmsid=496&vid=short_onecue&correlator="; - player = cloudinary.videoPlayer('player', { + player1 = cloudinary.videoPlayer('player', { cloud_name: 'demo', ads: { adTagUrl: adTagUrl, @@ -38,12 +38,22 @@ } }); - player.playlistByTag('video_race', { + player1.source('elephants'); + + player2 = cloudinary.videoPlayer('player-playlist', { + cloud_name: 'demo', + ads: { + adTagUrl: adTagUrl, + debug: true + } + }); + + player2.playlistByTag('video_race', { autoAdvance: true, repeat: true, }).then(function() { var divElem = document.querySelector("#playlist-data"); - var list = player.playlist().list().map(function(source) { + var list = player2.playlist().list().map(function(source) { return source.publicId() }).join(', '); @@ -62,17 +72,33 @@

    Cloudinary Video Player

    VAST and VPAID

    - - -

    +
    +

    Single video with ads

    + +
    + +
    +

    Playlist with Ads

    + + + +

    +

    Ads and monetization documentation @@ -84,6 +110,16 @@

    Example Code:

    <video id="player" + playsinline + controls + muted + class="cld-video-player" + width="500"> + </video> + + <video + id="player-playlist" + playsinline controls muted autoplay @@ -94,36 +130,30 @@

    Example Code:

    - // Initialize player - var player = cloudinary.videoPlayer('player', { - cloud_name: 'demo' , - plugins: { - vastClient: { - adTagUrl: "https://rtr.innovid.com/r1.5554946ab01d97.36996823;cb={random_number}", - adCancelTimeout: 5000, - adsEnabled: true, - playAdAlways: true - } + player1 = cloudinary.videoPlayer('player', { + cloud_name: 'demo', + ads: { + adTagUrl: AD_TAG_URL, + debug: true + } + }); + + player1.source('elephants'); + + player2 = cloudinary.videoPlayer('player-playlist', { + cloud_name: 'demo', + ads: { + adTagUrl: AD_TAG_URL, + debug: true } }); - // Pass a sorter to sort list in alphabetical order by publicId - var sorter = function(a, b) { - if (a.publicId < b.publicId) return 1; - if (a.publicId > b.publicId) return -1; - return 0; - }; - - // Fetch playlist by tag. Since this operation involves an API call - // the function returns a Promise when the operation completes. - // The return value is 'player'. - player.playlistByTag('demo', { - sorter: sorter, - autoAdvance: 0, - repeat: true + player2.playlistByTag('video_race', { + autoAdvance: true, + repeat: true, }).then(function() { var divElem = document.querySelector("#playlist-data"); - var list = player.playlist().list().map(function(source) { + var list = player2.playlist().list().map(function(source) { return source.publicId() }).join(', '); diff --git a/index.html b/index.html new file mode 100644 index 00000000..06b751ef --- /dev/null +++ b/index.html @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/package-lock.json b/package-lock.json index 40797ca9..c2bd95ee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,15 +9,15 @@ "version": "1.10.5", "license": "MIT", "dependencies": { - "@cloudinary/url-gen": "^1.10.2", + "@cloudinary/url-gen": "^1.16.1", "cloudinary-video-analytics": "1.4.3", - "dashjs": "^4.7.1", + "dashjs": "^4.7.3", "lodash": "^4.17.21", "uuid": "^9.0.0", - "video.js": "8.5.2", - "videojs-contrib-ads": "^7.3.2", - "videojs-ima": "^2.1.0", - "videojs-per-source-behaviors": "^3.0.0", + "video.js": "8.10.0", + "videojs-contrib-ads": "^7.3.3", + "videojs-ima": "^2.3.0", + "videojs-per-source-behaviors": "^3.0.1", "webfontloader": "^1.6.28" }, "devDependencies": { @@ -562,7 +562,6 @@ "version": "7.23.0", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", - "dev": true, "bin": { "parser": "bin/babel-parser.js" }, @@ -1857,19 +1856,19 @@ "dev": true }, "node_modules/@cloudinary/transformation-builder-sdk": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@cloudinary/transformation-builder-sdk/-/transformation-builder-sdk-1.7.0.tgz", - "integrity": "sha512-/FLYApYLTextepNq7R3I+nMkisAVnf+ELU0+M1Zfi4vB64n4pUT7Z4jgTfmDbMhnNsGRitLHTdiRkAoVeApxZw==", + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/@cloudinary/transformation-builder-sdk/-/transformation-builder-sdk-1.10.3.tgz", + "integrity": "sha512-ybLvj4+WDK50BcPAa1aBKJ/etWiesmBzcEJxVorMjus+fKej10TlHmsUIqFltKAj55MRodXijV+iuzKeErU1Zg==", "dependencies": { "@cloudinary/url-gen": "^1.7.0" } }, "node_modules/@cloudinary/url-gen": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/@cloudinary/url-gen/-/url-gen-1.12.0.tgz", - "integrity": "sha512-L22tftVId19g8irjuHuXOdTCWC8p45ZiejtFeuqmOtwW7nkPH1vHt5MaEvDM4DiljnW0CKZJxE9RNjyxgx1/+w==", + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/@cloudinary/url-gen/-/url-gen-1.16.1.tgz", + "integrity": "sha512-lRg0LlRJKrHHhSAedwR7u4YDvPQ2zuyXH3FpmyD8Ki6+/pcRVEJmkokKYnnbRNsu40smXFNJa6IHqjxsfbMmQg==", "dependencies": { - "@cloudinary/transformation-builder-sdk": "^1.7.0" + "@cloudinary/transformation-builder-sdk": "^1.10.3" } }, "node_modules/@cnakazawa/watch": { @@ -5029,7 +5028,6 @@ "version": "0.3.3", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "dev": true, "dependencies": { "@jridgewell/set-array": "^1.0.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -5043,7 +5041,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", - "dev": true, "engines": { "node": ">=6.0.0" } @@ -5052,7 +5049,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "dev": true, "engines": { "node": ">=6.0.0" } @@ -5061,7 +5057,6 @@ "version": "0.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", - "dev": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" @@ -5070,19 +5065,28 @@ "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.15", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.20", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", - "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@jsdoc/salty": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.7.tgz", + "integrity": "sha512-mh8LbS9d4Jq84KLw8pzho7XC2q2/IJGiJss3xwRoLD1A+EE16SjN4PfaG4jRCzKegTFLlN0Zd8SdUPE6XdoPFg==", + "dependencies": { + "lodash": "^4.17.21" + }, + "engines": { + "node": ">=v12.0.0" + } + }, "node_modules/@leichtgewicht/ip-codec": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", @@ -5575,6 +5579,25 @@ "integrity": "sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==", "dev": true }, + "node_modules/@types/linkify-it": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.5.tgz", + "integrity": "sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw==" + }, + "node_modules/@types/markdown-it": { + "version": "12.2.3", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", + "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", + "dependencies": { + "@types/linkify-it": "*", + "@types/mdurl": "*" + } + }, + "node_modules/@types/mdurl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.5.tgz", + "integrity": "sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA==" + }, "node_modules/@types/mime": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.4.tgz", @@ -5721,17 +5744,17 @@ } }, "node_modules/@videojs/http-streaming": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/@videojs/http-streaming/-/http-streaming-3.5.3.tgz", - "integrity": "sha512-dty8lsZk9QPc0i4It79tjWsmPiaC3FpgARFM0vJGko4k3yKNZIYkAk8kjiDRfkAQH/HZ3rYi5dDTriFNzwSsIg==", + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/@videojs/http-streaming/-/http-streaming-3.10.0.tgz", + "integrity": "sha512-Lf1rmhTalV4Gw0bJqHmH4lfk/FlepUDs9smuMtorblAYnqDlE2tbUOb7sBXVYoXGdbWbdTW8jH2cnS+6HWYJ4Q==", "dependencies": { "@babel/runtime": "^7.12.5", "@videojs/vhs-utils": "4.0.0", "aes-decrypter": "4.0.1", "global": "^4.4.0", "m3u8-parser": "^7.1.0", - "mpd-parser": "^1.1.1", - "mux.js": "7.0.0", + "mpd-parser": "^1.3.0", + "mux.js": "7.0.2", "video.js": "^7 || ^8" }, "engines": { @@ -5742,46 +5765,6 @@ "video.js": "^7 || ^8" } }, - "node_modules/@videojs/http-streaming/node_modules/m3u8-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/m3u8-parser/-/m3u8-parser-7.1.0.tgz", - "integrity": "sha512-7N+pk79EH4oLKPEYdgRXgAsKDyA/VCo0qCHlUwacttQA0WqsjZQYmNfywMvjlY9MpEBVZEt0jKFd73Kv15EBYQ==", - "dependencies": { - "@babel/runtime": "^7.12.5", - "@videojs/vhs-utils": "^3.0.5", - "global": "^4.4.0" - } - }, - "node_modules/@videojs/http-streaming/node_modules/m3u8-parser/node_modules/@videojs/vhs-utils": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@videojs/vhs-utils/-/vhs-utils-3.0.5.tgz", - "integrity": "sha512-PKVgdo8/GReqdx512F+ombhS+Bzogiofy1LgAj4tN8PfdBx3HSS7V5WfJotKTqtOWGwVfSWsrYN/t09/DSryrw==", - "dependencies": { - "@babel/runtime": "^7.12.5", - "global": "^4.4.0", - "url-toolkit": "^2.2.1" - }, - "engines": { - "node": ">=8", - "npm": ">=5" - } - }, - "node_modules/@videojs/http-streaming/node_modules/mux.js": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/mux.js/-/mux.js-7.0.0.tgz", - "integrity": "sha512-DeZmr+3NDrO02k4SREtl4VB5GyGPCz2fzMjDxBIlamkxffSTLge97rtNMoonnmFHTp96QggDucUtKv3fmyObrA==", - "dependencies": { - "@babel/runtime": "^7.11.2", - "global": "^4.4.0" - }, - "bin": { - "muxjs-transmux": "bin/transmux.js" - }, - "engines": { - "node": ">=8", - "npm": ">=5" - } - }, "node_modules/@videojs/vhs-utils": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@videojs/vhs-utils/-/vhs-utils-4.0.0.tgz", @@ -6014,6 +5997,11 @@ "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", "dev": true }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -6260,7 +6248,6 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, "dependencies": { "sprintf-js": "~1.0.2" } @@ -6305,6 +6292,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/array-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", + "integrity": "sha512-zHjL5SZa68hkKHBFBK6DJCTtr9sfTCPCaph/L7tMSLcTFgy+zX7E+6q5UArbtOtMBCtxdICpfTCspRse+ywyXA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/array-flatten": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", @@ -6317,6 +6312,14 @@ "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", "dev": true }, + "node_modules/array-slice": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", + "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", @@ -6402,6 +6405,11 @@ "node": ">=4" } }, + "node_modules/async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==" + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -6776,8 +6784,7 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/base": { "version": "0.11.2", @@ -6916,6 +6923,11 @@ "readable-stream": "^3.4.0" } }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, "node_modules/body-parser": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", @@ -6976,8 +6988,7 @@ "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "dev": true + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" }, "node_modules/boolean": { "version": "3.2.0", @@ -6989,7 +7000,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -7120,8 +7130,7 @@ "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, "node_modules/bundlewatch": { "version": "0.3.3", @@ -7273,7 +7282,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", - "dev": true, "dependencies": { "pascal-case": "^3.1.2", "tslib": "^2.0.3" @@ -7366,6 +7374,17 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/catharsis": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", + "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", + "dependencies": { + "lodash": "^4.17.15" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -7395,6 +7414,108 @@ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, + "node_modules/cheerio": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz", + "integrity": "sha512-8/MzidM6G/TgRelkzDG13y3Y9LxBjCb+8yOEZ9+wwq5gVF2w2pV0wmHvjfT0RvuxGyR7UEuK36r+yYMbT4uKgA==", + "dependencies": { + "css-select": "~1.2.0", + "dom-serializer": "~0.1.0", + "entities": "~1.1.1", + "htmlparser2": "^3.9.1", + "lodash.assignin": "^4.0.9", + "lodash.bind": "^4.1.4", + "lodash.defaults": "^4.0.1", + "lodash.filter": "^4.4.0", + "lodash.flatten": "^4.2.0", + "lodash.foreach": "^4.3.0", + "lodash.map": "^4.4.0", + "lodash.merge": "^4.4.0", + "lodash.pick": "^4.2.1", + "lodash.reduce": "^4.4.0", + "lodash.reject": "^4.4.0", + "lodash.some": "^4.4.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cheerio/node_modules/css-select": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "integrity": "sha512-dUQOBoqdR7QwV90WysXPLXG5LO7nhYBgiWVfxF80DKPF8zx1t/pUd2FYy73emg3zrjtM6dzmYgbHKfV2rxiHQA==", + "dependencies": { + "boolbase": "~1.0.0", + "css-what": "2.1", + "domutils": "1.5.1", + "nth-check": "~1.0.1" + } + }, + "node_modules/cheerio/node_modules/css-what": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", + "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==", + "engines": { + "node": "*" + } + }, + "node_modules/cheerio/node_modules/dom-serializer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", + "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", + "dependencies": { + "domelementtype": "^1.3.0", + "entities": "^1.1.1" + } + }, + "node_modules/cheerio/node_modules/domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + }, + "node_modules/cheerio/node_modules/domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "dependencies": { + "domelementtype": "1" + } + }, + "node_modules/cheerio/node_modules/domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha512-gSu5Oi/I+3wDENBsOWBiRK1eoGxcywYSqg3rR960/+EfY0CF4EX1VPkgHOZ3WiS/Jg2DtliF6BhWcHlfpYUcGw==", + "dependencies": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "node_modules/cheerio/node_modules/entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" + }, + "node_modules/cheerio/node_modules/htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "dependencies": { + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" + } + }, + "node_modules/cheerio/node_modules/nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "dependencies": { + "boolbase": "~1.0.0" + } + }, "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -7571,7 +7692,6 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.2.tgz", "integrity": "sha512-JVJbM+f3d3Q704rF4bqQ5UUyTtuJ0JRKNbTKVEeujCCBoMdkEi+V+e8oktO9qGQNSvHrFTM6JZRXrUvGR1czww==", - "dev": true, "dependencies": { "source-map": "~0.6.0" }, @@ -7579,6 +7699,93 @@ "node": ">= 10.0" } }, + "node_modules/clean-jsdoc-theme": { + "version": "4.2.17", + "resolved": "https://registry.npmjs.org/clean-jsdoc-theme/-/clean-jsdoc-theme-4.2.17.tgz", + "integrity": "sha512-5SbJNXcQHUXd7N13g+3OpGFiBQdxz36xwEP3p1r1vbo/apLcDRtugaFdUZ56H6Rvlb68Q33EChoBkajSlnD11w==", + "dependencies": { + "@jsdoc/salty": "^0.2.4", + "fs-extra": "^10.1.0", + "html-minifier-terser": "^7.2.0", + "klaw-sync": "^6.0.0", + "lodash": "^4.17.21", + "showdown": "^2.1.0" + }, + "peerDependencies": { + "jsdoc": ">=3.x <=4.x" + } + }, + "node_modules/clean-jsdoc-theme/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "engines": { + "node": ">=14" + } + }, + "node_modules/clean-jsdoc-theme/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/clean-jsdoc-theme/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/clean-jsdoc-theme/node_modules/html-minifier-terser": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-7.2.0.tgz", + "integrity": "sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA==", + "dependencies": { + "camel-case": "^4.1.2", + "clean-css": "~5.3.2", + "commander": "^10.0.0", + "entities": "^4.4.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.15.1" + }, + "bin": { + "html-minifier-terser": "cli.js" + }, + "engines": { + "node": "^14.13.1 || >=16.0.0" + } + }, + "node_modules/clean-jsdoc-theme/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/clean-jsdoc-theme/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/cli-cursor": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", @@ -7745,6 +7952,14 @@ "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", "dev": true }, + "node_modules/colors": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "integrity": "sha512-ENwblkFQpqqia6b++zLD/KUWafYlVY/UNnAp7oz7LY7E924wmpye416wBOmvv/HMWzl8gL1kJlfvId/1Dg176w==", + "engines": { + "node": ">=0.1.90" + } + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -7851,8 +8066,7 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "node_modules/connect-history-api-fallback": { "version": "2.0.0", @@ -8485,17 +8699,19 @@ } }, "node_modules/dashjs": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/dashjs/-/dashjs-4.7.2.tgz", - "integrity": "sha512-DSoSEK3Mvbw+u8Sf6xQW0f86dnC7Vn9CIk5oXeHr4zsJ6TiJK81/q3nNsGm2C9q9y9S0du+aR+AkKM04Bdt5BQ==", + "version": "4.7.3", + "resolved": "https://registry.npmjs.org/dashjs/-/dashjs-4.7.3.tgz", + "integrity": "sha512-jauFKD2wK0ZOAK6KJTEh8/SoTrdUoMW5V37HZacTDVso110vAKyJtXV+qfakEPavkPb8KEadiGGp+HC1i/XMFQ==", "dependencies": { "bcp-47-match": "^1.0.3", "bcp-47-normalize": "^1.1.1", + "clean-jsdoc-theme": "^4.2.10", "codem-isoboxer": "0.3.9", "es6-promise": "^4.2.8", "fast-deep-equal": "2.0.1", + "foodoc": "^0.0.9", "html-entities": "^1.2.1", - "imsc": "^1.1.3", + "imsc": "^1.1.4", "localforage": "^1.7.1", "path-browserify": "^1.0.1", "ua-parser-js": "^1.0.2" @@ -8515,6 +8731,14 @@ "node": ">=10" } }, + "node_modules/dateformat": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz", + "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==", + "engines": { + "node": "*" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -8798,6 +9022,14 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -8883,7 +9115,6 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", - "dev": true, "dependencies": { "domelementtype": "^2.0.1", "domhandler": "^4.2.0", @@ -8902,7 +9133,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "dev": true, "funding": [ { "type": "github", @@ -8935,7 +9165,6 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "dev": true, "dependencies": { "domelementtype": "^2.2.0" }, @@ -8950,7 +9179,6 @@ "version": "2.8.0", "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dev": true, "dependencies": { "dom-serializer": "^1.0.1", "domelementtype": "^2.2.0", @@ -8964,7 +9192,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", - "dev": true, "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" @@ -8985,8 +9212,7 @@ "node_modules/duplexer": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", - "dev": true + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" }, "node_modules/ee-first": { "version": "1.1.1", @@ -9062,7 +9288,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "dev": true, "funding": { "url": "https://github.com/fb55/entities?sponsor=1" } @@ -9214,7 +9439,6 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, "engines": { "node": ">=0.8.0" } @@ -9419,7 +9643,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" @@ -9497,6 +9720,11 @@ "node": ">= 0.6" } }, + "node_modules/eventemitter2": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", + "integrity": "sha512-K7J4xq5xAD5jHsGM5ReWXRTFa3JRGofHiMcVgQ8PRwgWxzjHpMWCIzsmyf60+mh8KLsqYPcjUMa0AC4hd6lPyQ==" + }, "node_modules/eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", @@ -9604,7 +9832,6 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true, "engines": { "node": ">= 0.8.0" } @@ -10220,6 +10447,11 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/file-sync-cmp": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/file-sync-cmp/-/file-sync-cmp-0.1.1.tgz", + "integrity": "sha512-0k45oWBokCqh2MOexeYKpyqmGKG+8mQ2Wd8iawx+uWd/weWJQAZ6SoPybagdCI4xFisag8iAR77WPm4h3pTfxA==" + }, "node_modules/file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", @@ -10434,57 +10666,288 @@ "node": ">=6" } }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, - "bin": { - "flat": "cli.js" + "node_modules/findup-sync": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-5.0.0.tgz", + "integrity": "sha512-MzwXju70AuyflbgeOhzvQWAvvQdo1XL0A9bVvlXsYcFEBM87WR4OakL4OfZq+QRmr+duJubio+UtNQCPsVESzQ==", + "dependencies": { + "detect-file": "^1.0.0", + "is-glob": "^4.0.3", + "micromatch": "^4.0.4", + "resolve-dir": "^1.0.1" + }, + "engines": { + "node": ">= 10.13.0" } }, - "node_modules/flat-cache": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", - "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", - "dev": true, + "node_modules/findup-sync/node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dependencies": { - "flatted": "^2.0.0", - "rimraf": "2.6.3", - "write": "1.0.3" + "fill-range": "^7.0.1" }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/flatted": { + "node_modules/findup-sync/node_modules/expand-tilde": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", - "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", - "dev": true - }, - "node_modules/follow-redirects": { - "version": "1.15.3", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", - "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==", + "dependencies": { + "homedir-polyfill": "^1.0.1" }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } + "engines": { + "node": ">=0.10.0" } }, - "node_modules/for-each": { - "version": "0.3.3", + "node_modules/findup-sync/node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/findup-sync/node_modules/global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "dependencies": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/findup-sync/node_modules/global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==", + "dependencies": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/findup-sync/node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/findup-sync/node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/findup-sync/node_modules/resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==", + "dependencies": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/findup-sync/node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/fined": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz", + "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==", + "dependencies": { + "expand-tilde": "^2.0.2", + "is-plain-object": "^2.0.3", + "object.defaults": "^1.1.0", + "object.pick": "^1.2.0", + "parse-filepath": "^1.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/fined/node_modules/expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==", + "dependencies": { + "homedir-polyfill": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/flagged-respawn": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", + "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "dev": true, + "dependencies": { + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/flatted": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", + "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", + "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/foodoc": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/foodoc/-/foodoc-0.0.9.tgz", + "integrity": "sha512-TjswPE8Vd8Wu1AAwu/aet/g0FlxCtEfWmkbFXppMq1FmgdwvH33U/jPJkIggAM7RoLPTB5UvNB5Cgg0PII/smQ==", + "dependencies": { + "cheerio": "^0.22.0", + "extend": "^3.0.1", + "glob": "^7.1.2", + "grunt": "^1.0.2", + "grunt-contrib-clean": "^1.1.0", + "grunt-contrib-copy": "^1.0.0", + "grunt-contrib-cssmin": "^2.2.1", + "grunt-contrib-uglify": "^3.3.0", + "handlebars": "^4.0.11", + "handlebars-layouts": "^3.1.4", + "jsdoc": "^3.5.5", + "lunr": "^1.0.0", + "moment": "^2.22.1", + "sanitize-html": "^1.18.2" + } + }, + "node_modules/foodoc/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/foodoc/node_modules/jsdoc": { + "version": "3.6.11", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.11.tgz", + "integrity": "sha512-8UCU0TYeIYD9KeLzEcAu2q8N/mx9O3phAGl32nmHlE0LpaJL71mMkP4d+QE5zWfNt50qheHtOZ0qoxVrsX5TUg==", + "dependencies": { + "@babel/parser": "^7.9.4", + "@types/markdown-it": "^12.2.3", + "bluebird": "^3.7.2", + "catharsis": "^0.9.0", + "escape-string-regexp": "^2.0.0", + "js2xmlparser": "^4.0.2", + "klaw": "^3.0.0", + "markdown-it": "^12.3.2", + "markdown-it-anchor": "^8.4.1", + "marked": "^4.0.10", + "mkdirp": "^1.0.4", + "requizzle": "^0.2.3", + "strip-json-comments": "^3.1.0", + "taffydb": "2.6.2", + "underscore": "~1.13.2" + }, + "bin": { + "jsdoc": "jsdoc.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/foodoc/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/foodoc/node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/for-each": { + "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", "dev": true, @@ -10496,7 +10959,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -10595,8 +11057,7 @@ "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/fsevents": { "version": "1.2.13", @@ -10621,7 +11082,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -10741,6 +11201,14 @@ "node": ">=0.10.0" } }, + "node_modules/getobject": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/getobject/-/getobject-1.0.2.tgz", + "integrity": "sha512-2zblDBaFcb3rB4rF77XVnuINOE2h2k/OnqXAiy0IrTxUfV1iFp3la33oAQVY9pCpWU268WFYVt2t71hlMuLsOg==", + "engines": { + "node": ">=10" + } + }, "node_modules/git-raw-commits": { "version": "2.0.11", "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.11.tgz", @@ -10873,7 +11341,6 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -11050,33 +11517,491 @@ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, "engines": { - "node": ">=8" + "node": ">=8" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/growly": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", + "integrity": "sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw==", + "dev": true, + "optional": true + }, + "node_modules/grunt": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/grunt/-/grunt-1.6.1.tgz", + "integrity": "sha512-/ABUy3gYWu5iBmrUSRBP97JLpQUm0GgVveDCp6t3yRNIoltIYw7rEj3g5y1o2PGPR2vfTRGa7WC/LZHLTXnEzA==", + "dependencies": { + "dateformat": "~4.6.2", + "eventemitter2": "~0.4.13", + "exit": "~0.1.2", + "findup-sync": "~5.0.0", + "glob": "~7.1.6", + "grunt-cli": "~1.4.3", + "grunt-known-options": "~2.0.0", + "grunt-legacy-log": "~3.0.0", + "grunt-legacy-util": "~2.0.1", + "iconv-lite": "~0.6.3", + "js-yaml": "~3.14.0", + "minimatch": "~3.0.4", + "nopt": "~3.0.6" + }, + "bin": { + "grunt": "bin/grunt" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/grunt-cli": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.4.3.tgz", + "integrity": "sha512-9Dtx/AhVeB4LYzsViCjUQkd0Kw0McN2gYpdmGYKtE2a5Yt7v1Q+HYZVWhqXc/kGnxlMtqKDxSwotiGeFmkrCoQ==", + "dependencies": { + "grunt-known-options": "~2.0.0", + "interpret": "~1.1.0", + "liftup": "~3.0.1", + "nopt": "~4.0.1", + "v8flags": "~3.2.0" + }, + "bin": { + "grunt": "bin/grunt" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/grunt-cli/node_modules/interpret": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", + "integrity": "sha512-CLM8SNMDu7C5psFCn6Wg/tgpj/bKAg7hc2gWqcuR9OD5Ft9PhBpIu8PLicPeis+xDd6YX2ncI8MCA64I9tftIA==" + }, + "node_modules/grunt-cli/node_modules/nopt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", + "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", + "dependencies": { + "abbrev": "1", + "osenv": "^0.1.4" + }, + "bin": { + "nopt": "bin/nopt.js" + } + }, + "node_modules/grunt-contrib-clean": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/grunt-contrib-clean/-/grunt-contrib-clean-1.1.0.tgz", + "integrity": "sha512-tET+TYTd8vCtKeGwbLjoH8+SdI8ngVzGbPr7vlWkewG7mYYHIccd2Ldxq+PK3DyBp5Www3ugdkfsjoNKUl5MTg==", + "dependencies": { + "async": "^1.5.2", + "rimraf": "^2.5.1" + }, + "engines": { + "node": ">= 0.10.0" + }, + "peerDependencies": { + "grunt": ">=0.4.5" + } + }, + "node_modules/grunt-contrib-copy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/grunt-contrib-copy/-/grunt-contrib-copy-1.0.0.tgz", + "integrity": "sha512-gFRFUB0ZbLcjKb67Magz1yOHGBkyU6uL29hiEW1tdQ9gQt72NuMKIy/kS6dsCbV0cZ0maNCb0s6y+uT1FKU7jA==", + "dependencies": { + "chalk": "^1.1.1", + "file-sync-cmp": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/grunt-contrib-copy/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/grunt-contrib-copy/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/grunt-contrib-copy/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/grunt-contrib-copy/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/grunt-contrib-copy/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/grunt-contrib-cssmin": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/grunt-contrib-cssmin/-/grunt-contrib-cssmin-2.2.1.tgz", + "integrity": "sha512-IXNomhQ5ekVZbDbj/ik5YccoD9khU6LT2fDXqO1+/Txjq8cp0tQKjVS8i8EAbHOrSDkL7/UD6A7b+xj98gqh9w==", + "dependencies": { + "chalk": "^1.0.0", + "clean-css": "~4.1.1", + "maxmin": "^2.1.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/grunt-contrib-cssmin/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/grunt-contrib-cssmin/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/grunt-contrib-cssmin/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/grunt-contrib-cssmin/node_modules/clean-css": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.1.11.tgz", + "integrity": "sha512-a3ZEe58u+LizPdSCHM0jIGeKu1hN+oqqXXc1i70mnV0x2Ox3/ho1pE6Y8HD6yhDts5lEQs028H9kutlihP77uQ==", + "dependencies": { + "source-map": "0.5.x" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/grunt-contrib-cssmin/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/grunt-contrib-cssmin/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/grunt-contrib-cssmin/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/grunt-contrib-uglify": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/grunt-contrib-uglify/-/grunt-contrib-uglify-3.4.0.tgz", + "integrity": "sha512-UXsTpeP0pytpTYlmll3RDndsRXfdwmrf1tI/AtD/PrArQAzGmKMvj83aVt3D8egWlE6KqPjsJBLCCvfC52LI/A==", + "dependencies": { + "chalk": "^1.0.0", + "maxmin": "^2.1.0", + "uglify-js": "~3.4.0", + "uri-path": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/grunt-contrib-uglify/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/grunt-contrib-uglify/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/grunt-contrib-uglify/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/grunt-contrib-uglify/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/grunt-contrib-uglify/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/grunt-known-options": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/grunt-known-options/-/grunt-known-options-2.0.0.tgz", + "integrity": "sha512-GD7cTz0I4SAede1/+pAbmJRG44zFLPipVtdL9o3vqx9IEyb7b4/Y3s7r6ofI3CchR5GvYJ+8buCSioDv5dQLiA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/grunt-legacy-log": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-3.0.0.tgz", + "integrity": "sha512-GHZQzZmhyq0u3hr7aHW4qUH0xDzwp2YXldLPZTCjlOeGscAOWWPftZG3XioW8MasGp+OBRIu39LFx14SLjXRcA==", + "dependencies": { + "colors": "~1.1.2", + "grunt-legacy-log-utils": "~2.1.0", + "hooker": "~0.2.3", + "lodash": "~4.17.19" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/grunt-legacy-log-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-2.1.0.tgz", + "integrity": "sha512-lwquaPXJtKQk0rUM1IQAop5noEpwFqOXasVoedLeNzaibf/OPWjKYvvdqnEHNmU+0T0CaReAXIbGo747ZD+Aaw==", + "dependencies": { + "chalk": "~4.1.0", + "lodash": "~4.17.19" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/grunt-legacy-log-utils/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/grunt-legacy-log-utils/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/grunt-legacy-log-utils/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/grunt-legacy-log-utils/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/grunt-legacy-log-utils/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/grunt-legacy-log-utils/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/grunt-legacy-util": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-2.0.1.tgz", + "integrity": "sha512-2bQiD4fzXqX8rhNdXkAywCadeqiPiay0oQny77wA2F3WF4grPJXCvAcyoWUJV+po/b15glGkxuSiQCK299UC2w==", + "dependencies": { + "async": "~3.2.0", + "exit": "~0.1.2", + "getobject": "~1.0.0", + "hooker": "~0.2.3", + "lodash": "~4.17.21", + "underscore.string": "~3.3.5", + "which": "~2.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/grunt-legacy-util/node_modules/async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" + }, + "node_modules/grunt-legacy-util/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" } }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, + "node_modules/grunt/node_modules/glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", "dependencies": { - "get-intrinsic": "^1.1.3" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true + "node_modules/grunt/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } }, - "node_modules/growly": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", - "integrity": "sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw==", - "dev": true, - "optional": true + "node_modules/grunt/node_modules/minimatch": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz", + "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } }, "node_modules/gzip-size": { "version": "6.0.0", @@ -11099,6 +12024,34 @@ "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", "dev": true }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/handlebars-layouts": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/handlebars-layouts/-/handlebars-layouts-3.1.4.tgz", + "integrity": "sha512-2llBmvnj8ueOfxNHdRzJOcgalzZjYVd9+WAl93kPYmlX4WGx7FTHTzNxhK+i9YKY2OSjzfehgpLiIwP/OJr6tw==", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/hard-rejection": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", @@ -11112,7 +12065,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", - "dev": true, "dependencies": { "ansi-regex": "^2.0.0" }, @@ -11124,7 +12076,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -11241,7 +12192,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", - "dev": true, "dependencies": { "function-bind": "^1.1.2" }, @@ -11262,7 +12212,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", - "dev": true, "dependencies": { "parse-passwd": "^1.0.0" }, @@ -11270,6 +12219,14 @@ "node": ">=0.10.0" } }, + "node_modules/hooker": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz", + "integrity": "sha512-t+UerCsQviSymAInD01Pw+Dn/usmz1sRO+3Zk1+lx8eg+WKpD2ulcwWqHHL0+aseRBr+3+vIhiG1K1JTwaIcTA==", + "engines": { + "node": "*" + } + }, "node_modules/hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", @@ -11694,9 +12651,9 @@ } }, "node_modules/imsc": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/imsc/-/imsc-1.1.3.tgz", - "integrity": "sha512-IY0hMkVTNoqoYwKEp5UvNNKp/A5jeJUOrIO7judgOyhHT+xC6PA4VBOMAOhdtAYbMRHx9DTgI8p6Z6jhYQPFDA==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/imsc/-/imsc-1.1.5.tgz", + "integrity": "sha512-V8je+CGkcvGhgl2C1GlhqFFiUOIEdwXbXLiu1Fcubvvbo+g9inauqT3l0pNYXGoLPBj3jxtZz9t+wCopMkwadQ==", "dependencies": { "sax": "1.2.1" } @@ -11719,7 +12676,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -11728,14 +12684,12 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/ini": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" }, "node_modules/inquirer": { "version": "6.5.2", @@ -11832,6 +12786,18 @@ "node": ">= 10" } }, + "node_modules/is-absolute": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", + "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", + "dependencies": { + "is-relative": "^1.0.0", + "is-windows": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-accessor-descriptor": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.1.tgz", @@ -11960,7 +12926,6 @@ "version": "2.13.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", - "dev": true, "dependencies": { "hasown": "^2.0.0" }, @@ -12048,7 +13013,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -12080,7 +13044,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -12152,7 +13115,6 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, "dependencies": { "isobject": "^3.0.1" }, @@ -12182,6 +13144,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-relative": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", + "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", + "dependencies": { + "is-unc-path": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-shared-array-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", @@ -12269,6 +13242,17 @@ "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", "dev": true }, + "node_modules/is-unc-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", + "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", + "dependencies": { + "unc-path-regex": "^0.1.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-weakref": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", @@ -12285,7 +13269,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -12311,14 +13294,12 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "node_modules/isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -17357,7 +18338,6 @@ "version": "3.14.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -17366,6 +18346,76 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/js2xmlparser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", + "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", + "dependencies": { + "xmlcreate": "^2.0.4" + } + }, + "node_modules/jsdoc": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.2.tgz", + "integrity": "sha512-e8cIg2z62InH7azBBi3EsSEqrKx+nUtAS5bBcYTSpZFA+vhNPyhv8PTFZ0WsjOPDj04/dOLlm08EDcQJDqaGQg==", + "peer": true, + "dependencies": { + "@babel/parser": "^7.20.15", + "@jsdoc/salty": "^0.2.1", + "@types/markdown-it": "^12.2.3", + "bluebird": "^3.7.2", + "catharsis": "^0.9.0", + "escape-string-regexp": "^2.0.0", + "js2xmlparser": "^4.0.2", + "klaw": "^3.0.0", + "markdown-it": "^12.3.2", + "markdown-it-anchor": "^8.4.1", + "marked": "^4.0.10", + "mkdirp": "^1.0.4", + "requizzle": "^0.2.3", + "strip-json-comments": "^3.1.0", + "underscore": "~1.13.2" + }, + "bin": { + "jsdoc": "jsdoc.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/jsdoc/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jsdoc/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "peer": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jsdoc/node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "peer": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/jsdom": { "version": "16.7.0", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", @@ -17541,83 +18591,246 @@ "node": ">=0.10.0" } }, + "node_modules/klaw": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", + "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", + "dependencies": { + "graceful-fs": "^4.1.9" + } + }, + "node_modules/klaw-sync": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz", + "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==", + "dependencies": { + "graceful-fs": "^4.1.11" + } + }, "node_modules/kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", "dev": true, "engines": { - "node": ">=6" + "node": ">=6" + } + }, + "node_modules/klona": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", + "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/launch-editor": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.6.1.tgz", + "integrity": "sha512-eB/uXmFVpY4zezmGp5XtU21kwo7GBbKB+EQ+UZeWtGb9yAM5xt/Evk+lYH3eRNAtId+ej4u7TYPFZ07w4s7rRw==", + "dev": true, + "dependencies": { + "picocolors": "^1.0.0", + "shell-quote": "^1.8.1" + } + }, + "node_modules/lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha512-RE2g0b5VGZsOCFOCgP7omTRYFqydmZkBwl5oNnQ1lDYC57uyO9KqNnNVxT7COSHTxrRCWVcAVOcbjk+tvh/rgQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha512-YiGkH6EnGrDGqLMITnGjXtGmNtjoXw9SVUzcaos8RBi7Ps0VBylkq+vOcY9QE5poLasPCR849ucFUkl0UzUyOw==", + "dev": true, + "dependencies": { + "invert-kv": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lie": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", + "integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/liftup": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/liftup/-/liftup-3.0.1.tgz", + "integrity": "sha512-yRHaiQDizWSzoXk3APcA71eOI/UuhEkNN9DiW2Tt44mhYzX4joFoCZlxsSOF7RyeLlfqzFLQI1ngFq3ggMPhOw==", + "dependencies": { + "extend": "^3.0.2", + "findup-sync": "^4.0.0", + "fined": "^1.2.0", + "flagged-respawn": "^1.0.1", + "is-plain-object": "^2.0.4", + "object.map": "^1.0.1", + "rechoir": "^0.7.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/liftup/node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/liftup/node_modules/expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==", + "dependencies": { + "homedir-polyfill": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/liftup/node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "node_modules/klona": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", - "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", - "dev": true, + "node_modules/liftup/node_modules/findup-sync": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-4.0.0.tgz", + "integrity": "sha512-6jvvn/12IC4quLBL1KNokxC7wWTvYncaVUYSoxWw7YykPLuRrnv4qdHcSOywOI5RpkOVGeQRtWM8/q+G6W6qfQ==", + "dependencies": { + "detect-file": "^1.0.0", + "is-glob": "^4.0.0", + "micromatch": "^4.0.2", + "resolve-dir": "^1.0.1" + }, "engines": { "node": ">= 8" } }, - "node_modules/launch-editor": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.6.1.tgz", - "integrity": "sha512-eB/uXmFVpY4zezmGp5XtU21kwo7GBbKB+EQ+UZeWtGb9yAM5xt/Evk+lYH3eRNAtId+ej4u7TYPFZ07w4s7rRw==", - "dev": true, + "node_modules/liftup/node_modules/global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", "dependencies": { - "picocolors": "^1.0.0", - "shell-quote": "^1.8.1" + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/lazy-cache": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", - "integrity": "sha512-RE2g0b5VGZsOCFOCgP7omTRYFqydmZkBwl5oNnQ1lDYC57uyO9KqNnNVxT7COSHTxrRCWVcAVOcbjk+tvh/rgQ==", - "dev": true, + "node_modules/liftup/node_modules/global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==", + "dependencies": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + }, "engines": { "node": ">=0.10.0" } }, - "node_modules/lcid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "integrity": "sha512-YiGkH6EnGrDGqLMITnGjXtGmNtjoXw9SVUzcaos8RBi7Ps0VBylkq+vOcY9QE5poLasPCR849ucFUkl0UzUyOw==", - "dev": true, + "node_modules/liftup/node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/liftup/node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dependencies": { - "invert-kv": "^1.0.0" + "braces": "^3.0.2", + "picomatch": "^2.3.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=8.6" } }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, + "node_modules/liftup/node_modules/rechoir": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", + "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", + "dependencies": { + "resolve": "^1.9.0" + }, "engines": { - "node": ">=6" + "node": ">= 0.10" } }, - "node_modules/levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", - "dev": true, + "node_modules/liftup/node_modules/resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==", "dependencies": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" }, "engines": { - "node": ">= 0.8.0" + "node": ">=0.10.0" } }, - "node_modules/lie": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", - "integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==", + "node_modules/liftup/node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dependencies": { - "immediate": "~3.0.5" + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" } }, "node_modules/lilconfig": { @@ -17635,6 +18848,14 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, + "node_modules/linkify-it": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", + "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", + "dependencies": { + "uc.micro": "^1.0.1" + } + }, "node_modules/load-json-file": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", @@ -17782,6 +19003,16 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash.assignin": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz", + "integrity": "sha512-yX/rx6d/UTVh7sSVWVSIMjfnz95evAgDFdb1ZozC35I9mSFCkmzptOzevxjgbQUsc78NR44LVHWjsoMQXy9FDg==" + }, + "node_modules/lodash.bind": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz", + "integrity": "sha512-lxdsn7xxlCymgLYo1gGvVrfHmkjDiyqVv62FAeF2i5ta72BipE1SLxw8hPEPLhD4/247Ijw07UQH7Hq/chT5LA==" + }, "node_modules/lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", @@ -17794,17 +19025,31 @@ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "dev": true }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" + }, "node_modules/lodash.escape": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-4.0.1.tgz", "integrity": "sha512-nXEOnb/jK9g0DYMr1/Xvq6l5xMD7GDG55+GSYIYmS0G4tBk/hURD4JR9WCavs04t33WmJx9kCyp9vJ+mr4BOUw==", "dev": true }, + "node_modules/lodash.filter": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz", + "integrity": "sha512-pXYUy7PR8BCLwX5mgJ/aNtyOvuJTdZAo9EQFUvMIYugqmJxnrYaANvTbgndOzHSCSR0wnlBBfRXJL5SbWxo3FQ==" + }, "node_modules/lodash.flatten": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==", - "dev": true + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==" + }, + "node_modules/lodash.foreach": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz", + "integrity": "sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ==" }, "node_modules/lodash.invokemap": { "version": "4.6.0", @@ -17830,6 +19075,11 @@ "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==", "dev": true }, + "node_modules/lodash.map": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz", + "integrity": "sha512-worNHGKLDetmcEYDvh2stPCrrQRkP20E4l0iIS7F8EvzMqBBi7ltvFN5m1HvTf1P7Jk1txKhvFcmYsCr8O2F1Q==" + }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -17839,8 +19089,7 @@ "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, "node_modules/lodash.mergewith": { "version": "4.6.2", @@ -17848,18 +19097,38 @@ "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", "dev": true }, + "node_modules/lodash.pick": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", + "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==" + }, "node_modules/lodash.pullall": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.pullall/-/lodash.pullall-4.2.0.tgz", "integrity": "sha512-VhqxBKH0ZxPpLhiu68YD1KnHmbhQJQctcipvmFnqIBDYzcIHzf3Zpu0tpeOKtR4x76p9yohc506eGdOjTmyIBg==", "dev": true }, + "node_modules/lodash.reduce": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz", + "integrity": "sha512-6raRe2vxCYBhpBu+B+TtNGUzah+hQjVdu3E17wfusjyrXBka2nBS8OH/gjVZ5PvHOhWmIZTYri09Z6n/QfnNMw==" + }, + "node_modules/lodash.reject": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.reject/-/lodash.reject-4.6.0.tgz", + "integrity": "sha512-qkTuvgEzYdyhiJBx42YPzPo71R1aEr0z79kAv7Ixg8wPFEjgRgJdUsGMG3Hf3OYSF/kHI79XhNlt+5Ar6OzwxQ==" + }, "node_modules/lodash.snakecase": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==", "dev": true }, + "node_modules/lodash.some": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz", + "integrity": "sha512-j7MJE+TuT51q9ggt4fSgVqro163BEFjAt3u97IqU+JA2DkWl80nFTrowzLpZ/BnpN7rrl0JA/593NAdd8p/scQ==" + }, "node_modules/lodash.startcase": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", @@ -17900,7 +19169,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "dev": true, "dependencies": { "tslib": "^2.0.3" } @@ -17914,10 +19182,15 @@ "yallist": "^3.0.2" } }, + "node_modules/lunr": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-1.0.0.tgz", + "integrity": "sha512-vGgr9YUMBfL1izpsb4RASwPz58JSSdmcTocuCs2v0PyGU3e7CDJWuS5psl4O2m9t0CsNemeR+jhxu2xNkXCM2A==" + }, "node_modules/m3u8-parser": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/m3u8-parser/-/m3u8-parser-6.2.0.tgz", - "integrity": "sha512-qlC00JTxYOxawcqg+RB8jbyNwL3foY/nCY61kyWP+RCuJE9APLeqB/nSlTjb4Mg0yRmyERgjswpdQxMvkeoDrg==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/m3u8-parser/-/m3u8-parser-7.1.0.tgz", + "integrity": "sha512-7N+pk79EH4oLKPEYdgRXgAsKDyA/VCo0qCHlUwacttQA0WqsjZQYmNfywMvjlY9MpEBVZEt0jKFd73Kv15EBYQ==", "dependencies": { "@babel/runtime": "^7.12.5", "@videojs/vhs-utils": "^3.0.5", @@ -17962,6 +19235,25 @@ "semver": "bin/semver.js" } }, + "node_modules/make-iterator": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", + "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/make-iterator/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/makeerror": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", @@ -17975,7 +19267,6 @@ "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -18004,6 +19295,54 @@ "node": ">=0.10.0" } }, + "node_modules/markdown-it": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", + "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", + "dependencies": { + "argparse": "^2.0.1", + "entities": "~2.1.0", + "linkify-it": "^3.0.1", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + }, + "bin": { + "markdown-it": "bin/markdown-it.js" + } + }, + "node_modules/markdown-it-anchor": { + "version": "8.6.7", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz", + "integrity": "sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==", + "peerDependencies": { + "@types/markdown-it": "*", + "markdown-it": "*" + } + }, + "node_modules/markdown-it/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/markdown-it/node_modules/entities": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/marked": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 12" + } + }, "node_modules/matcher": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", @@ -18028,12 +19367,104 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/maxmin": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/maxmin/-/maxmin-2.1.0.tgz", + "integrity": "sha512-NWlApBjW9az9qRPaeg7CX4sQBWwytqz32bIEo1PW9pRW+kBP9KLRfJO3UC+TV31EcQZEUq7eMzikC7zt3zPJcw==", + "dependencies": { + "chalk": "^1.0.0", + "figures": "^1.0.1", + "gzip-size": "^3.0.0", + "pretty-bytes": "^3.0.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/maxmin/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/maxmin/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/maxmin/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/maxmin/node_modules/figures": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "integrity": "sha512-UxKlfCRuCBxSXU4C6t9scbDyWZ4VlaFFdojKtzJuSkuOBQ5CNFum+zZXFwHjo+CxBC1t6zlYPgHIgFjL8ggoEQ==", + "dependencies": { + "escape-string-regexp": "^1.0.5", + "object-assign": "^4.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/maxmin/node_modules/gzip-size": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-3.0.0.tgz", + "integrity": "sha512-6s8trQiK+OMzSaCSVXX+iqIcLV9tC+E73jrJrJTyS4h/AJhlxHvzFKqM1YLDJWRGgHX8uLkBeXkA0njNj39L4w==", + "dependencies": { + "duplexer": "^0.1.1" + }, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/maxmin/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/maxmin/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/mdn-data": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", "dev": true }, + "node_modules/mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==" + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -18312,7 +19743,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -18324,7 +19754,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -18423,32 +19852,26 @@ "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", "dev": true }, - "node_modules/mpd-parser": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/mpd-parser/-/mpd-parser-1.2.2.tgz", - "integrity": "sha512-QCfB1koOoZw6E5La1cx+W/Yd0EZlRhHMqMr4TAJez0eRTuPDzPM5FWoiOqjyo37W+ISPLzmfJACSbJFEBjbL4Q==", - "dependencies": { - "@babel/runtime": "^7.12.5", - "@videojs/vhs-utils": "^3.0.5", - "@xmldom/xmldom": "^0.8.3", - "global": "^4.4.0" - }, - "bin": { - "mpd-to-m3u8-json": "bin/parse.js" + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "engines": { + "node": "*" } }, - "node_modules/mpd-parser/node_modules/@videojs/vhs-utils": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@videojs/vhs-utils/-/vhs-utils-3.0.5.tgz", - "integrity": "sha512-PKVgdo8/GReqdx512F+ombhS+Bzogiofy1LgAj4tN8PfdBx3HSS7V5WfJotKTqtOWGwVfSWsrYN/t09/DSryrw==", + "node_modules/mpd-parser": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/mpd-parser/-/mpd-parser-1.3.0.tgz", + "integrity": "sha512-WgeIwxAqkmb9uTn4ClicXpEQYCEduDqRKfmUdp4X8vmghKfBNXZLYpREn9eqrDx/Tf5LhzRcJLSpi4ohfV742Q==", "dependencies": { "@babel/runtime": "^7.12.5", - "global": "^4.4.0", - "url-toolkit": "^2.2.1" - }, - "engines": { - "node": ">=8", - "npm": ">=5" + "@videojs/vhs-utils": "^4.0.0", + "@xmldom/xmldom": "^0.8.3", + "global": "^4.4.0" + }, + "bin": { + "mpd-to-m3u8-json": "bin/parse.js" } }, "node_modules/mrmime": { @@ -18486,9 +19909,9 @@ "dev": true }, "node_modules/mux.js": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/mux.js/-/mux.js-6.3.0.tgz", - "integrity": "sha512-/QTkbSAP2+w1nxV+qTcumSDN5PA98P0tjrADijIzQHe85oBK3Akhy9AHlH0ne/GombLMz1rLyvVsmrgRxoPDrQ==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/mux.js/-/mux.js-7.0.2.tgz", + "integrity": "sha512-CM6+QuyDbc0qW1OfEjkd2+jVKzTXF+z5VOKH0eZxtZtnrG/ilkW/U7l7IXGtBNLASF9sKZMcK1u669cq50Qq0A==", "dependencies": { "@babel/runtime": "^7.11.2", "global": "^4.4.0" @@ -18575,8 +19998,7 @@ "node_modules/neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, "node_modules/nice-try": { "version": "1.0.5", @@ -18588,7 +20010,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "dev": true, "dependencies": { "lower-case": "^2.0.2", "tslib": "^2.0.3" @@ -18707,6 +20128,17 @@ "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", "dev": true }, + "node_modules/nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha512-4GUt3kSEYmk4ITxzB/b9vaIDfUVWN/Ml1Fwl11IlnIG2iaJ9O6WXZ9SrYM9NLI8OCBieN2Y8SWC2oJV0RQ7qYg==", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + } + }, "node_modules/normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -18786,7 +20218,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -18797,6 +20228,14 @@ "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==", "dev": true }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-copy": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", @@ -18893,6 +20332,31 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object.defaults": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", + "integrity": "sha512-c/K0mw/F11k4dEUBMW8naXUuBuhxRCfG7W+yFy8EcijU/rSmazOUd1XAEEe6bC0OuXY4HUKjTJv7xbxIMqdxrA==", + "dependencies": { + "array-each": "^1.0.1", + "array-slice": "^1.0.0", + "for-own": "^1.0.0", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.defaults/node_modules/for-own": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", + "integrity": "sha512-0OABksIGrxKK8K4kynWkQ7y1zounQxP+CWnyclVwj81KW3vlLlGUx57DKGcP/LH216GzqnstnPocF16Nxs0Ycg==", + "dependencies": { + "for-in": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object.getownpropertydescriptors": { "version": "2.1.7", "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.7.tgz", @@ -18912,11 +20376,33 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object.map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", + "integrity": "sha512-3+mAJu2PLfnSVGHwIWubpOFLscJANBKuB/6A4CxBstc4aqwQY0FWcsppuy4jU5GSB95yES5JHSI+33AWuS4k6w==", + "dependencies": { + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.map/node_modules/for-own": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", + "integrity": "sha512-0OABksIGrxKK8K4kynWkQ7y1zounQxP+CWnyclVwj81KW3vlLlGUx57DKGcP/LH216GzqnstnPocF16Nxs0Ycg==", + "dependencies": { + "for-in": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object.pick": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==", - "dev": true, "dependencies": { "isobject": "^3.0.1" }, @@ -18955,7 +20441,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "dependencies": { "wrappy": "1" } @@ -19022,7 +20507,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -19120,11 +20604,19 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "dev": true, "engines": { "node": ">=0.10.0" } }, + "node_modules/osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "dependencies": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, "node_modules/p-each-series": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz", @@ -19214,7 +20706,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", - "dev": true, "dependencies": { "dot-case": "^3.0.4", "tslib": "^2.0.3" @@ -19232,6 +20723,19 @@ "node": ">=6" } }, + "node_modules/parse-filepath": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", + "integrity": "sha512-FwdRXKCohSVeXqwtYonZTXtbGJKrn+HNyWDYVcp5yuJlesTwNH4rsmRZ+GrKAPJ5bLpRxESMeS+Rl0VCHRvB2Q==", + "dependencies": { + "is-absolute": "^1.0.0", + "map-cache": "^0.2.0", + "path-root": "^0.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -19254,11 +20758,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", - "dev": true, "engines": { "node": ">=0.10.0" } }, + "node_modules/parse-srcset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz", + "integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==" + }, "node_modules/parse5": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", @@ -19278,7 +20786,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", - "dev": true, "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" @@ -19311,7 +20818,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -19334,8 +20840,26 @@ "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/path-root": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", + "integrity": "sha512-QLcPegTHF11axjfojBIoDygmS2E3Lf+8+jI6wOVmNVenrKSo3mFdSGiIgdSHenczw3wPtlVMQaFVwGmM7BJdtg==", + "dependencies": { + "path-root-regex": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-root-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", + "integrity": "sha512-4GlJ6rZDhQZFE0DPVKh0e9jmZ5egZfxTkp7bcRDuPlJXbAwhxcl2dINPUAsjLdejqaLsCeg8axcLjIbvBjN4pQ==", + "engines": { + "node": ">=0.10.0" + } }, "node_modules/path-to-regexp": { "version": "0.1.7", @@ -19368,7 +20892,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, "engines": { "node": ">=8.6" }, @@ -20028,6 +21551,17 @@ "node": ">= 0.8.0" } }, + "node_modules/pretty-bytes": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-3.0.1.tgz", + "integrity": "sha512-eb7ZAeUTgfh294cElcu51w+OTRp/6ItW758LjwJSK72LDevcuJn0P4eD71PLMDGPwwatXmAmYHTkzvpKlJE3ow==", + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/pretty-error": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", @@ -20524,7 +22058,6 @@ "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -20706,7 +22239,6 @@ "version": "0.2.7", "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", - "dev": true, "engines": { "node": ">= 0.10" } @@ -20790,11 +22322,18 @@ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", "dev": true }, + "node_modules/requizzle": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz", + "integrity": "sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==", + "dependencies": { + "lodash": "^4.17.21" + } + }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", @@ -20935,7 +22474,6 @@ "version": "2.6.3", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "dev": true, "dependencies": { "glob": "^7.1.3" }, @@ -21055,7 +22593,6 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, "funding": [ { "type": "github", @@ -21105,8 +22642,7 @@ "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/sane": { "version": "4.1.0", @@ -21183,6 +22719,63 @@ "node": ">=4" } }, + "node_modules/sanitize-html": { + "version": "1.27.5", + "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-1.27.5.tgz", + "integrity": "sha512-M4M5iXDAUEcZKLXkmk90zSYWEtk5NH3JmojQxKxV371fnMh+x9t1rqdmXaGoyEHw3z/X/8vnFhKjGL5xFGOJ3A==", + "dependencies": { + "htmlparser2": "^4.1.0", + "lodash": "^4.17.15", + "parse-srcset": "^1.0.2", + "postcss": "^7.0.27" + } + }, + "node_modules/sanitize-html/node_modules/domhandler": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-3.3.0.tgz", + "integrity": "sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==", + "dependencies": { + "domelementtype": "^2.0.1" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/sanitize-html/node_modules/htmlparser2": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-4.1.0.tgz", + "integrity": "sha512-4zDq1a1zhE4gQso/c5LP1OtrhYTncXNSpvJYtWJBtXAETPlMfi3IFNjGuQbYLuVY4ZR0QMqRVvo4Pdy9KLyP8Q==", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^3.0.0", + "domutils": "^2.0.0", + "entities": "^2.0.0" + } + }, + "node_modules/sanitize-html/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==" + }, + "node_modules/sanitize-html/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, "node_modules/sass": { "version": "1.69.5", "resolved": "https://registry.npmjs.org/sass/-/sass-1.69.5.tgz", @@ -21698,6 +23291,29 @@ "dev": true, "optional": true }, + "node_modules/showdown": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/showdown/-/showdown-2.1.0.tgz", + "integrity": "sha512-/6NVYu4U819R2pUIk79n67SYgJHWCce0a5xTP979WbNp0FL9MN1I1QK662IDU1b6JzKTvmhgI7T7JYIxBi3kMQ==", + "dependencies": { + "commander": "^9.0.0" + }, + "bin": { + "showdown": "bin/showdown.js" + }, + "funding": { + "type": "individual", + "url": "https://www.paypal.me/tiviesantos" + } + }, + "node_modules/showdown/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "engines": { + "node": "^12.20.0 || >=14" + } + }, "node_modules/shx": { "version": "0.3.4", "resolved": "https://registry.npmjs.org/shx/-/shx-0.3.4.tgz", @@ -21951,7 +23567,6 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -22037,7 +23652,6 @@ "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -22148,8 +23762,7 @@ "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" }, "node_modules/stable": { "version": "0.1.8", @@ -22230,7 +23843,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, "dependencies": { "safe-buffer": "~5.2.0" } @@ -22453,7 +24065,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -22553,6 +24164,11 @@ "node": ">=6" } }, + "node_modules/taffydb": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", + "integrity": "sha512-y3JaeRSplks6NYQuCOj3ZFMO3j60rTwbuKCvZxsAraGYH2epusatvZ0baZYA01WsGqJBq/Dl6vOrMUJqyMj8kA==" + }, "node_modules/tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", @@ -22637,7 +24253,6 @@ "version": "5.24.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.24.0.tgz", "integrity": "sha512-ZpGR4Hy3+wBEzVEnHvstMvqpD/nABNelQn/z2r0fjVWGQsN3bpOLzQlqDxmb4CDZnXq5lpjnQ+mHQLAOpfM5iw==", - "dev": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", @@ -22754,7 +24369,6 @@ "version": "8.11.2", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", - "dev": true, "bin": { "acorn": "bin/acorn" }, @@ -22765,8 +24379,7 @@ "node_modules/terser/node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" }, "node_modules/test-exclude": { "version": "5.2.3", @@ -23000,8 +24613,7 @@ "node_modules/tslib": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "node_modules/tunnel": { "version": "0.0.6", @@ -23165,6 +24777,31 @@ "node": "*" } }, + "node_modules/uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==" + }, + "node_modules/uglify-js": { + "version": "3.4.10", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.10.tgz", + "integrity": "sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw==", + "dependencies": { + "commander": "~2.19.0", + "source-map": "~0.6.1" + }, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/uglify-js/node_modules/commander": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", + "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==" + }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -23190,6 +24827,36 @@ "through": "^2.3.8" } }, + "node_modules/unc-path-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", + "integrity": "sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/underscore": { + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", + "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==" + }, + "node_modules/underscore.string": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.6.tgz", + "integrity": "sha512-VoC83HWXmCrF6rgkyxS9GHv8W9Q5nhMKho+OadDJGzL2oDYbYEppBaCMH6pFlwLeqj2QS+hhkw2kpXkSdD1JxQ==", + "dependencies": { + "sprintf-js": "^1.1.1", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/underscore.string/node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==" + }, "node_modules/undici": { "version": "5.28.2", "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.2.tgz", @@ -23389,6 +25056,14 @@ "punycode": "^2.1.0" } }, + "node_modules/uri-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/uri-path/-/uri-path-1.0.0.tgz", + "integrity": "sha512-8pMuAn4KacYdGMkFaoQARicp4HSw24/DHOVKWqVRJ8LhhAwPPFpdGvdL9184JVmUwe7vz7Z9n6IqI6t5n2ELdg==", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/urix": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", @@ -23468,8 +25143,7 @@ "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "node_modules/util.promisify": { "version": "1.1.2", @@ -23545,6 +25219,17 @@ "node": ">= 8" } }, + "node_modules/v8flags": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz", + "integrity": "sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg==", + "dependencies": { + "homedir-polyfill": "^1.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -23565,20 +25250,20 @@ } }, "node_modules/video.js": { - "version": "8.5.2", - "resolved": "https://registry.npmjs.org/video.js/-/video.js-8.5.2.tgz", - "integrity": "sha512-6/uNXQV3xSaKLpaPf/bVvr7omd+82sKUp0RMBgIt4PxHIe28GtX+O+GcNfI2fuwBvcDRDqk5Ei5AG9bJJOpulA==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/video.js/-/video.js-8.10.0.tgz", + "integrity": "sha512-7UeG/flj/pp8tNGW8WKPP1VJb3x2FgLoqUWzpZqkoq5YIyf6MNzmIrKtxprl438T5RVkcj+OzV8IX4jYSAn4Sw==", "dependencies": { "@babel/runtime": "^7.12.5", - "@videojs/http-streaming": "3.5.3", + "@videojs/http-streaming": "3.10.0", "@videojs/vhs-utils": "^4.0.0", "@videojs/xhr": "2.6.0", "aes-decrypter": "^4.0.1", "global": "4.4.0", "keycode": "2.2.0", - "m3u8-parser": "^6.0.0", - "mpd-parser": "^1.0.1", - "mux.js": "^6.2.0", + "m3u8-parser": "^7.1.0", + "mpd-parser": "^1.2.2", + "mux.js": "^7.0.1", "safe-json-parse": "4.0.0", "videojs-contrib-quality-levels": "4.0.0", "videojs-font": "4.1.0", @@ -23586,9 +25271,9 @@ } }, "node_modules/videojs-contrib-ads": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/videojs-contrib-ads/-/videojs-contrib-ads-7.3.2.tgz", - "integrity": "sha512-O5zSqP9GePr8XDUgCKQoRdDr5dUX5GEbNx7s2R7LC/Pxn8L10x3RNCrijzO56bli7Vr5wAlTJgAk1dR5lqQ44A==", + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/videojs-contrib-ads/-/videojs-contrib-ads-7.3.3.tgz", + "integrity": "sha512-Uk628G4aQHRwJPaSV1g6x6G6tEQN4WPO36jjmfSHFPvX71sU/rwOVO/aZ5GNFsNp+xiiz6g9kqeBY1dWvS5/4A==", "dependencies": { "global": "^4.3.2" }, @@ -23621,14 +25306,14 @@ "integrity": "sha512-X1LuPfLZPisPLrANIAKCknZbZu5obVM/ylfd1CN+SsCmPZQ3UMDPcvLTpPBJxcBuTpHQq2MO1QCFt7p8spnZ/w==" }, "node_modules/videojs-ima": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/videojs-ima/-/videojs-ima-2.2.0.tgz", - "integrity": "sha512-VKg7mZsaiW89ko2Sjfz/sDcCbCTjj2OAkg8CakvCs9ZiVg20g6QYVtEWj249V/HTm0/1X01GEPk9hnIrRzgd+A==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/videojs-ima/-/videojs-ima-2.3.0.tgz", + "integrity": "sha512-8r0BZGT+WCTO6PePyKZHikV79Ojqh4yLMx4+DmPyXeRcKUVsQ7Va0R7Ok8GRcA8Zy3l1PM6jzLrD/W1rwKhZ8g==", "dependencies": { "@hapi/cryptiles": "^5.1.0", "can-autoplay": "^3.0.2", "extend": ">=3.0.2", - "videojs-contrib-ads": "^6.9.0 || ^7" + "videojs-contrib-ads": "^6.9.0" }, "engines": { "node": ">=0.8.0" @@ -23637,19 +25322,7 @@ "video.js": "^5.19.2 || ^6 || ^7 || ^8" } }, - "node_modules/videojs-per-source-behaviors": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/videojs-per-source-behaviors/-/videojs-per-source-behaviors-3.0.0.tgz", - "integrity": "sha512-nzigAb5JKpc8OXkN+VvZwyfJPe6xcsxeFMTRB1bVkIfYDMMSd9XqQbn/vigPSGiNe5peGDZ9zbqVRupI20w6vw==", - "dependencies": { - "global": "^4.4.0", - "video.js": "^7.17.0" - }, - "engines": { - "node": ">=4.4.0" - } - }, - "node_modules/videojs-per-source-behaviors/node_modules/@videojs/http-streaming": { + "node_modules/videojs-ima/node_modules/@videojs/http-streaming": { "version": "2.16.2", "resolved": "https://registry.npmjs.org/@videojs/http-streaming/-/http-streaming-2.16.2.tgz", "integrity": "sha512-etPTUdCFu7gUWc+1XcbiPr+lrhOcBu3rV5OL1M+3PDW89zskScAkkcdqYzP4pFodBPye/ydamQoTDScOnElw5A==", @@ -23671,7 +25344,27 @@ "video.js": "^6 || ^7" } }, - "node_modules/videojs-per-source-behaviors/node_modules/@videojs/vhs-utils": { + "node_modules/videojs-ima/node_modules/@videojs/http-streaming/node_modules/video.js": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/video.js/-/video.js-7.21.5.tgz", + "integrity": "sha512-WRq86tXZKrThA9mK+IR+v4tIQVVvnb5LhvL71fD2AX7TxVOPdaeK1X/wyuUruBqWaOG3w2sZXoMY6HF2Jlo9qA==", + "dependencies": { + "@babel/runtime": "^7.12.5", + "@videojs/http-streaming": "2.16.2", + "@videojs/vhs-utils": "^3.0.4", + "@videojs/xhr": "2.6.0", + "aes-decrypter": "3.1.3", + "global": "^4.4.0", + "keycode": "^2.2.0", + "m3u8-parser": "4.8.0", + "mpd-parser": "0.22.1", + "mux.js": "6.0.1", + "safe-json-parse": "4.0.0", + "videojs-font": "3.2.0", + "videojs-vtt.js": "^0.15.5" + } + }, + "node_modules/videojs-ima/node_modules/@videojs/vhs-utils": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/@videojs/vhs-utils/-/vhs-utils-3.0.5.tgz", "integrity": "sha512-PKVgdo8/GReqdx512F+ombhS+Bzogiofy1LgAj4tN8PfdBx3HSS7V5WfJotKTqtOWGwVfSWsrYN/t09/DSryrw==", @@ -23685,7 +25378,7 @@ "npm": ">=5" } }, - "node_modules/videojs-per-source-behaviors/node_modules/aes-decrypter": { + "node_modules/videojs-ima/node_modules/aes-decrypter": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/aes-decrypter/-/aes-decrypter-3.1.3.tgz", "integrity": "sha512-VkG9g4BbhMBy+N5/XodDeV6F02chEk9IpgRTq/0bS80y4dzy79VH2Gtms02VXomf3HmyRe3yyJYkJ990ns+d6A==", @@ -23696,7 +25389,7 @@ "pkcs7": "^1.0.4" } }, - "node_modules/videojs-per-source-behaviors/node_modules/m3u8-parser": { + "node_modules/videojs-ima/node_modules/m3u8-parser": { "version": "4.8.0", "resolved": "https://registry.npmjs.org/m3u8-parser/-/m3u8-parser-4.8.0.tgz", "integrity": "sha512-UqA2a/Pw3liR6Df3gwxrqghCP17OpPlQj6RBPLYygf/ZSQ4MoSgvdvhvt35qV+3NaaA0FSZx93Ix+2brT1U7cA==", @@ -23706,7 +25399,7 @@ "global": "^4.4.0" } }, - "node_modules/videojs-per-source-behaviors/node_modules/mpd-parser": { + "node_modules/videojs-ima/node_modules/mpd-parser": { "version": "0.22.1", "resolved": "https://registry.npmjs.org/mpd-parser/-/mpd-parser-0.22.1.tgz", "integrity": "sha512-fwBebvpyPUU8bOzvhX0VQZgSohncbgYwUyJJoTSNpmy7ccD2ryiCvM7oRkn/xQH5cv73/xU7rJSNCLjdGFor0Q==", @@ -23720,7 +25413,7 @@ "mpd-to-m3u8-json": "bin/parse.js" } }, - "node_modules/videojs-per-source-behaviors/node_modules/mux.js": { + "node_modules/videojs-ima/node_modules/mux.js": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/mux.js/-/mux.js-6.0.1.tgz", "integrity": "sha512-22CHb59rH8pWGcPGW5Og7JngJ9s+z4XuSlYvnxhLuc58cA1WqGDQPzuG8I+sPm1/p0CdgpzVTaKW408k5DNn8w==", @@ -23736,7 +25429,20 @@ "npm": ">=5" } }, - "node_modules/videojs-per-source-behaviors/node_modules/video.js": { + "node_modules/videojs-ima/node_modules/videojs-contrib-ads": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/videojs-contrib-ads/-/videojs-contrib-ads-6.9.0.tgz", + "integrity": "sha512-nzKz+jhCGMTYffSNVYrmp9p70s05v6jUMOY3Z7DpVk3iFrWK4Zi/BIkokDWrMoHpKjdmCdKzfJVBT+CrUj6Spw==", + "dependencies": { + "global": "^4.3.2", + "video.js": "^6 || ^7" + }, + "engines": { + "node": ">=8", + "npm": ">=5" + } + }, + "node_modules/videojs-ima/node_modules/videojs-contrib-ads/node_modules/video.js": { "version": "7.21.5", "resolved": "https://registry.npmjs.org/video.js/-/video.js-7.21.5.tgz", "integrity": "sha512-WRq86tXZKrThA9mK+IR+v4tIQVVvnb5LhvL71fD2AX7TxVOPdaeK1X/wyuUruBqWaOG3w2sZXoMY6HF2Jlo9qA==", @@ -23756,11 +25462,23 @@ "videojs-vtt.js": "^0.15.5" } }, - "node_modules/videojs-per-source-behaviors/node_modules/videojs-font": { + "node_modules/videojs-ima/node_modules/videojs-font": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/videojs-font/-/videojs-font-3.2.0.tgz", "integrity": "sha512-g8vHMKK2/JGorSfqAZQUmYYNnXmfec4MLhwtEFS+mMs2IDY398GLysy6BH6K+aS1KMNu/xWZ8Sue/X/mdQPliA==" }, + "node_modules/videojs-per-source-behaviors": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/videojs-per-source-behaviors/-/videojs-per-source-behaviors-3.0.1.tgz", + "integrity": "sha512-M/s4qSP2OlQkAbElYstPdcAeLHXXtHlY1HNNQB2Cpa0c+oJFpLW595EOIXQ7B18agUv+POvLxuXVEGmq/SQOeA==", + "dependencies": { + "global": "^4.4.0", + "video.js": "^7 || ^8" + }, + "engines": { + "node": ">=4.4.0" + } + }, "node_modules/videojs-vtt.js": { "version": "0.15.5", "resolved": "https://registry.npmjs.org/videojs-vtt.js/-/videojs-vtt.js-0.15.5.tgz", @@ -24586,7 +26304,6 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, "dependencies": { "isexe": "^2.0.0" }, @@ -24650,6 +26367,11 @@ "node": ">=0.10.0" } }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==" + }, "node_modules/wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -24735,8 +26457,7 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/write": { "version": "1.0.3", @@ -24794,6 +26515,11 @@ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "dev": true }, + "node_modules/xmlcreate": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", + "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==" + }, "node_modules/y18n": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", diff --git a/package.json b/package.json index b1b3b08d..11da0cf9 100644 --- a/package.json +++ b/package.json @@ -52,32 +52,32 @@ "files": [ { "path": "./dist/cld-video-player.min.js", - "maxSize": "285kb" + "maxSize": "240kb" }, { "path": "./dist/cld-video-player.light.min.js", - "maxSize": "150kb" + "maxSize": "125kb" }, { "path": "./lib/cld-video-player.js", - "maxSize": "499kb" + "maxSize": "485kb" }, { "path": "./lib/videoPlayer.js", - "maxSize": "285kb" + "maxSize": "240kb" } ] }, "dependencies": { - "@cloudinary/url-gen": "^1.10.2", + "@cloudinary/url-gen": "^1.16.1", "cloudinary-video-analytics": "1.4.3", - "dashjs": "^4.7.1", + "dashjs": "^4.7.3", "lodash": "^4.17.21", "uuid": "^9.0.0", - "video.js": "8.5.2", - "videojs-contrib-ads": "^7.3.2", - "videojs-ima": "^2.1.0", - "videojs-per-source-behaviors": "^3.0.0", + "video.js": "8.10.0", + "videojs-contrib-ads": "^7.3.3", + "videojs-ima": "^2.3.0", + "videojs-per-source-behaviors": "^3.0.1", "webfontloader": "^1.6.28" }, "devDependencies": { diff --git a/src/assets/styles/components/playlist.scss b/src/assets/styles/components/playlist.scss deleted file mode 100644 index 95a47b29..00000000 --- a/src/assets/styles/components/playlist.scss +++ /dev/null @@ -1,213 +0,0 @@ -@import '../../../assets/styles/mixins/mixins.scss'; - -@mixin listLayout() { - .cld-plw-layout { - &.cld-plw-vertical, &.cld-plw-horizontal { - flex-direction: column; - - .cld-plw-panel { - flex-direction: column; - } - - .cld-plw-col-list { - width: inherit !important; - height: inherit !important; - max-height: 340px; - } - - .cld-plw-panel-item { - background: none !important; - - img { - display: block; - width: 150px; - height: 84px; - } - - .cld-plw-item-info-wrap { - left: 150px; - padding: 15px 10px; - top: 0; - right: 0; - width: auto; - } - - .cld-plw-item-title-next { - display: block; - } - - .cld-plw-item-title-curr { - display: block; - } - - .cld-plw-item-title { - white-space: normal; - } - } - } - - &.cld-plw-horizontal { - .cld-plw-col-list { - padding-top: 0; - overflow: auto; - } - - .cld-plw-panel { - position: initial; - } - - .cld-plw-panel-item { - max-width: none; - } - } - } -} - -.cld-plw-horizontal { - .cld-plw-col-list { - padding-top: 14.0625%; - position: relative; - } - - .cld-plw-panel { - overflow: auto; - position: absolute; - top: 0; - bottom: 0; - right: 0; - left: 0; - display: flex; - flex-direction: row; - } - - .cld-plw-panel-item { - background-image: none; - min-width: 25%; - max-width: 25%; - - &:first-child { - margin-left: 0; - } - - &:last-child { - margin-right: 0; - } - } -} - - -.cld-plw-vertical { - - display: flex; - flex-direction: row; - - &.cld-plw-layout { - > div:first-child { - flex: 1; - } - } - - .cld-plw-col-list { - display: flex; - flex-direction: column; - min-width: 20%; - } - - .cld-plw-panel { - height: 100%; - overflow: auto; - } - - .cld-plw-panel-item { - height: 25%; - - &:last-child { - margin-bottom: 0; - } - - } -} - -.cld-plw-item-title-next { - font-weight: bold; -} - -.cld-plw-item-info-wrap { - position: absolute; - bottom: 0; - left: 0; - padding: 5% 7%; - width: 100%; - transition: color .25s; - display: flex; - flex-direction: row; - justify-content: space-between; - box-sizing: border-box; - - .cld-video-player-skin-dark & { - text-shadow: 1px 1px 1px rgba(#000, .3); - - @media only screen and (max-width: 768px) { - background: var(--color-base);; - } - } -} - -.cld-plw-item-title { - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; - @include ellipsis; -} - -.cld-plw-item-duration { - margin-left: 10px; -} - -/* Default Playlist Layout */ - -.cld-plw-layout { - &.cld-plw-layout-fluid { - width: 100%; - } - - &.cld-plw-vertical { - flex-direction: row; - } - - &.cld-plw-horizontal { - flex-direction: column; - } -} - -.cld-plw-custom { - padding: 4px; - .cld-plw-panel-item { - background: none !important; - margin: 4px 0; - &:first-child { - margin-top: 0; - } - - &:last-child { - margin-bottom: 0; - } - - img { - display: block; - width: 150px; - } - - .cld-plw-item-info-wrap { - left: 160px; - } - - .cld-plw-item-title-next { - display: block; - } - } -} - -@media only screen and (max-width: 768px) { - @include listLayout(); -} diff --git a/src/assets/styles/main.scss b/src/assets/styles/main.scss index 33e206c7..fca6248f 100644 --- a/src/assets/styles/main.scss +++ b/src/assets/styles/main.scss @@ -8,7 +8,6 @@ $icon-font-path: '../icon-font' !default; @import '~video.root.js/dist/video-js.min.css'; @import 'mixins/skin'; @import 'icons'; -@import 'components/interaction-areas.scss'; @import 'components/loading-button.scss'; @import 'components/text-tracks.scss'; @@ -243,8 +242,9 @@ $icon-font-path: '../icon-font' !default; } .vjs-control-bar { - z-index: 2; + -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px); + z-index: 2; font-size: 120%; // responsive controls sizes, based on container width // font-size: clamp(100%, 2cqw, 150%); @@ -257,6 +257,7 @@ $icon-font-path: '../icon-font' !default; } .vjs-progress-control { + -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px); position: absolute; left: 0px; diff --git a/src/components/index.js b/src/components/index.js index a063341f..8dd2f8fa 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -1,19 +1,13 @@ import JumpForwardButton from './jumpButtons/jump-10-plus'; import JumpBackButton from './jumpButtons/jump-10-minus'; import LogoButton from './logoButton/logo-button'; -import PlaylistPanel from './playlist/panel/playlist-panel'; import ProgressControlEventsBlocker from './progress-control-events-blocker/progress-control-events-blocker'; -import RecommendationsOverlay from './recommendations-overlay'; -import ShoppablePanel from './shoppable-bar/panel/shoppable-panel'; import TitleBar from './title-bar/title-bar'; export { JumpForwardButton, JumpBackButton, LogoButton, - PlaylistPanel, ProgressControlEventsBlocker, - RecommendationsOverlay, - ShoppablePanel, TitleBar }; diff --git a/src/components/recommendations-overlay/index.js b/src/components/recommendations-overlay/index.js index c004ca22..2f88d92d 100644 --- a/src/components/recommendations-overlay/index.js +++ b/src/components/recommendations-overlay/index.js @@ -1,3 +1,11 @@ -import RecommendationsOverlay from './recommendations-overlay'; - -export default RecommendationsOverlay; +export default async function lazyRecommendationsOverlayComponent(player) { + try { + if (!player.getChild('recommendationsOverlay')) { + await import(/* webpackChunkName: "recommendations-overlay" */ './recommendations-overlay'); + player.addChild('recommendationsOverlay'); + } + return player; + } catch (error) { + console.error('Failed to load plugin:', error); + } +} diff --git a/src/components/recommendations-overlay/recommendations-overlay-primary-item.js b/src/components/recommendations-overlay/recommendations-overlay-primary-item.js index 4406724e..0baf883f 100644 --- a/src/components/recommendations-overlay/recommendations-overlay-primary-item.js +++ b/src/components/recommendations-overlay/recommendations-overlay-primary-item.js @@ -1,6 +1,6 @@ import videojs from 'video.js'; import RecommendationsOverlayItem from './recommendations-overlay-item'; -import componentUtils from '../component-utils'; +import componentUtils from 'components/component-utils'; // support VJS5 & VJS6 at the same time const dom = videojs.dom || videojs; diff --git a/src/components/shoppable-bar/panel/shoppable-panel.js b/src/components/shoppable-bar/panel/shoppable-panel.js index c39a5eea..d565d5d5 100644 --- a/src/components/shoppable-bar/panel/shoppable-panel.js +++ b/src/components/shoppable-bar/panel/shoppable-panel.js @@ -2,7 +2,6 @@ import videojs from 'video.js'; import { assign } from 'utils/assign'; import { throttle } from 'utils/throttle'; import { parseTime } from 'utils/time'; -import 'assets/styles/components/playlist.scss'; import ShoppablePanelItem from './shoppable-panel-item'; import ImageSource from '../../../plugins/cloudinary/models/image-source'; import { diff --git a/src/config/defaults.js b/src/config/defaults.js index 4ddc5cfb..937bd6e4 100644 --- a/src/config/defaults.js +++ b/src/config/defaults.js @@ -9,10 +9,8 @@ export default { playsinline: videojs.browser.IS_IOS, skin: 'dark', controls: false, - controlBar: { - pictureInPictureToggle: false, - chaptersButton: false - }, + chaptersButton: false, + pictureInPictureToggle: false, seekThumbnails: true, aiHighlightsGraph: false, preload: PRELOAD.AUTO, diff --git a/src/config/profiles/cldAdaptiveStream.json b/src/config/profiles/cldAdaptiveStream.json new file mode 100644 index 00000000..3f20af1c --- /dev/null +++ b/src/config/profiles/cldAdaptiveStream.json @@ -0,0 +1,21 @@ +{ + "playerOptions": { + "fluid": true, + "controls": true, + "controlBar": { + "fullscreenToggle": false + }, + "showJumpControls": true, + "hideContextMenu": false, + "floatingWhenNotVisible": "none" + }, + "sourceOptions": { + "chapters": true, + "sourceTypes": ["hls"], + "transformation": [ + { + "effect": ["volume:auto"] + } + ] + } +} diff --git a/src/config/profiles/cldDefault.json b/src/config/profiles/cldDefault.json new file mode 100644 index 00000000..c8c24b97 --- /dev/null +++ b/src/config/profiles/cldDefault.json @@ -0,0 +1,4 @@ +{ + "playerOptions": {}, + "sourceOptions": {} +} diff --git a/src/config/profiles/cldLooping.json b/src/config/profiles/cldLooping.json new file mode 100644 index 00000000..ab33fe5c --- /dev/null +++ b/src/config/profiles/cldLooping.json @@ -0,0 +1,12 @@ +{ + "playerOptions": { + "fluid": true, + "controls": false, + "muted": true, + "floatingWhenNotVisible": "none", + "hideContextMenu": false, + "autoplay": true, + "loop": true + }, + "sourceOptions": {} +} diff --git a/src/config/profiles/index.js b/src/config/profiles/index.js new file mode 100644 index 00000000..32df3c8e --- /dev/null +++ b/src/config/profiles/index.js @@ -0,0 +1,9 @@ +import cldDefault from './cldDefault.json'; +import cldLooping from './cldLooping.json'; +import cldAdaptiveStream from './cldAdaptiveStream.json'; + +export const defaultProfiles = { + cldDefault, + cldLooping, + cldAdaptiveStream +}; diff --git a/src/index.all.js b/src/index.all.js index 1f5565b2..a1cc6cfe 100644 --- a/src/index.all.js +++ b/src/index.all.js @@ -9,7 +9,12 @@ import cloudinary from './index.js'; export * from './index.js'; +export * from './plugins/chapters/chapters.js'; export * from './plugins/dash/videojs-dash.js'; +export * from './plugins/ima/ima.js'; +export * from './plugins/playlist/playlist.js'; export * from './plugins/styled-text-tracks/styled-text-tracks.js'; +export * from './plugins/interaction-areas/interaction-areas.service.js'; +export * from './components/shoppable-bar/shoppable-widget.js'; export default cloudinary; diff --git a/src/index.js b/src/index.js index d3b2c60d..f26e2466 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,6 @@ import 'assets/styles/main.scss'; import VideoPlayer from './video-player'; +import createVideoPlayerProfile from './video-player-profile'; import { assign } from 'utils/assign'; import { pick, convertKeysToSnakeCase } from './utils/object'; import { CLOUDINARY_CONFIG_PARAM } from './video-player.const'; @@ -33,10 +34,14 @@ const cloudinaryVideoPlayerConfig = config => ({ export const videoPlayer = getVideoPlayer(); export const videoPlayers = getVideoPlayers(); +const getVideoPlayerWithProfile = config => (id, playerOptions, ready) => createVideoPlayerProfile(id, getConfig(playerOptions, config), ready); +export const videoPlayerWithProfile = getVideoPlayerWithProfile(); + const cloudinary = { ...(window.cloudinary || {}), videoPlayer, videoPlayers, + videoPlayerWithProfile, Cloudinary: { // Backwards compatibility with SDK v1 new: cloudinaryVideoPlayerConfig diff --git a/src/mixins/playlistable.js b/src/mixins/playlistable.js deleted file mode 100644 index 58f7a8b2..00000000 --- a/src/mixins/playlistable.js +++ /dev/null @@ -1,104 +0,0 @@ -import Playlist from 'components/playlist/playlist'; -import { sliceProperties } from 'utils/slicing'; -import { normalizeJsonResponse } from 'utils/api'; -import { assign } from 'utils/assign'; -import { extendCloudinaryConfig, getCloudinaryUrl } from '../plugins/cloudinary/common'; - -const LIST_BY_TAG_PARAMS = { format: 'json', resource_type: 'video', type: 'list' }; - -const Playlistable = (superclass) => class extends superclass { - - constructor(player, options = {}) { - super(); - - const _chainTarget = sliceProperties(options, 'chainTarget').chainTarget; - let _playlist = null; - let _playlistDisposer = null; - - this.playlist = (sources, options = {}) => { - if (sources === undefined) { - return _playlist; - } - - if (this.playlist()) { - this.disposePlaylist(); - } - - createPlaylist(sources, options); - player.trigger('playlistcreated'); - - return _chainTarget; - }; - - this.disposePlaylist = () => { - player.removeClass('vjs-playlist'); - const playlist = this.playlist(); - _playlist = undefined; - playlist.dispose(); - this.off('cldsourcechanged', _playlistDisposer); - }; - - const createPlaylist = (sources, options) => { - if (sources instanceof Playlist) { - _playlist = sources; - _playlist.resetState(); - _playlist.currentIndex(_playlist.currentIndex()); - } else { - _playlist = new Playlist(this, sources, options); - _playlist.currentIndex(0); - } - - _playlistDisposer = addSourceChangedListener(); - player.addClass('vjs-playlist'); - }; - - const addSourceChangedListener = () => { - const disposer = () => { - if (this.playlist() && - !this.playlist().currentSource().contains(this.player.currentSource())) { - this.disposePlaylist(); - } - }; - - this.on('cldsourcechanged', disposer); - - return disposer; - }; - } - - playlistByTag(tag, options = {}) { - return this.sourcesByTag(tag, options) - .then((sources) => this.playlist(sources, options)); - } - - sourcesByTag(tag, options = {}) { - const url = getCloudinaryUrl(tag, extendCloudinaryConfig(this.cloudinaryConfig(), LIST_BY_TAG_PARAMS)); - - return fetch(url) - .then((result) => result.json()) - .then((json) => { - const resources = normalizeJsonResponse(json.resources); - - if (options.sorter) { - resources.sort(options.sorter); - } - - const sources = resources.map((resource) => { - let sourceParams = options.sourceParams || {}; - - if (typeof sourceParams === 'function') { - sourceParams = sourceParams(resource); - } - - const info = resource.context && resource.context.custom || {}; - - const source = assign({ info }, sourceParams, { publicId: resource.publicId }); - return this.buildSource(source); - }); - - return sources; - }); - } -}; - -export default Playlistable; diff --git a/src/plugins/chapters/chapters.js b/src/plugins/chapters/chapters.js new file mode 100644 index 00000000..b9b0930b --- /dev/null +++ b/src/plugins/chapters/chapters.js @@ -0,0 +1,237 @@ +import videojs from 'video.js'; + +import './chapters.scss'; +import { extendCloudinaryConfig, getCloudinaryUrl } from '../cloudinary/common'; + +/** + * Chapters plugin. + * + * @function chapters + * @param {Object} [options={}] + * An object of options left to the plugin author to define. + * @param {Player} player + * A Video.js player object. + */ +const chapters = function chapters(options, player) { + player.addClass('vjs-chapters'); + player.chapters = new ChaptersPlugin(player, options); +}; + +/** + * Chapters + */ +const ChaptersPlugin = (function () { + + /** + * Plugin class constructor, called by videojs on + * ready event. + * + * @function constructor + * @param {Player} player + * A Video.js player object. + * + * @param {Object} [options={}] + * A plain object containing options for the plugin. + */ + function ChaptersPlugin(player, options) { + this.player = player; + this.options = options; + this.player.one('loadedmetadata', this.initializeChapters.bind(this)); + return this; + } + + ChaptersPlugin.prototype.src = function src(options) { + this.resetPlugin(); + this.options = options; + this.player.one('loadedmetadata', this.initializeChapters.bind(this)); + }; + + ChaptersPlugin.prototype.detach = function detach() { + this.resetPlugin(); + }; + + ChaptersPlugin.prototype.resetPlugin = function resetPlugin() { + if (this.chaptersTrack) { + this.player.$('.vjs-control-bar-chapter-display').remove(); + this.player.$('.vjs-chapter-display').remove(); + this.player.$$('.vjs-chapter-marker').forEach((el) => el.remove()); + this.player.removeRemoteTextTrack(this.chaptersTrack); + delete this.chaptersTrack; + } + }; + + ChaptersPlugin.prototype.getChaptersFileUrlByName = function getChaptersFileUrlByName() { + const currentPublicId = this.player.cloudinary.currentPublicId(); + + if (!currentPublicId) { + return null; + } + + const fullUrl = getCloudinaryUrl( + `${currentPublicId}-chapters.vtt`, + extendCloudinaryConfig(this.player.cloudinary.cloudinaryConfig(), { resource_type: 'raw', version: '1' }), + ); + return `${fullUrl}?t=${Date.now()}`; + }; + + /** + * Bootstrap the plugin. + */ + ChaptersPlugin.prototype.initializeChapters = async function initializeChapters() { + const chaptersUrl = this.options === true ? this.getChaptersFileUrlByName() : this.options.url; + + if (chaptersUrl) { + // Fetch chapters VTT from URL + const chaptersTrack = { + kind: 'chapters', + src: chaptersUrl, + default: true + }; + const textTrack = this.player.addRemoteTextTrack(chaptersTrack); + + textTrack.addEventListener('load', () => { + this.chaptersTrack = textTrack.track; + this.setupChaptersDisplays(); + }); + } else if (Object.entries(this.options).length) { + // Setup chapters from options + const textTrack = this.player.addRemoteTextTrack({ + kind: 'chapters', + default: true + }); + + // required for Safari to display the captions + // https://github.com/videojs/video.js/issues/8519 + await new Promise(resolve => setTimeout(resolve, 100)); + + const end = this.player.duration(); + Object.entries(this.options).forEach((entry, index, arr) => { + const cue = new VTTCue( + parseFloat(entry[0]), + parseFloat(arr[index + 1] ? arr[index + 1][0] : end), + entry[1] + ); + textTrack.track.addCue(cue); + }); + + this.chaptersTrack = textTrack.track; + this.setupChaptersDisplays(); + if (this.player.controlBar.chaptersButton) { + this.player.controlBar.chaptersButton.update(); + } + } + }; + + /** + * Setup chapter displays. + */ + ChaptersPlugin.prototype.setupChaptersDisplays = function initializeChapters() { + this.setupProgressBarMarkers(); + this.setupProgressBarChapter(); + this.setupControlBarChapter(); + }; + + /** + * Setup the controlbar chapter display. + */ + ChaptersPlugin.prototype.setupControlBarChapter = function setupControlBarChapter() { + const controlBarChapterHolder = + this.player.$('.vjs-control-bar-chapter-display') || document.createElement('div'); + controlBarChapterHolder.setAttribute('class', 'vjs-control-bar-chapter-display'); + + const wrapper = this.player.$('.vjs-control-bar .vjs-spacer'); + wrapper.innerHTML = ''; + wrapper.classList.add('vjs-control-bar-chapter-wrapper'); + wrapper.appendChild(controlBarChapterHolder); + + this.chaptersTrack.addEventListener('cuechange', () => { + const activeCues = Array.from(this.chaptersTrack.activeCues); // Safari needs Array.from() + controlBarChapterHolder.innerHTML = + activeCues.length > 0 ? activeCues[0].text : ''; + }); + }; + + /** + * Setup the progress bar markers. + */ + ChaptersPlugin.prototype.setupProgressBarMarkers = function setupProgressBarMarkers() { + const total = this.player.duration(); + const { seekBar } = this.player.controlBar.progressControl; + + Array.from(this.chaptersTrack.cues).forEach(marker => { // Safari needs Array.from() + if (marker.startTime !== 0) { + const markerTime = marker.startTime; + const left = (markerTime / total) * 100 + '%'; + + const markerEl = videojs.dom.createEl('div', undefined, { + class: 'vjs-chapter-marker', + style: `left: ${left}` + }); + + seekBar.el().append(markerEl); + } + }); + }; + + /** + * Setup the progrees bar on-hover chapter display. + */ + ChaptersPlugin.prototype.setupProgressBarChapter = function setupProgressBarChapter() { + const chapterEl = videojs.dom.createEl('div', undefined, { + class: 'vjs-chapter-display', + style: `max-width: ${this.player.$('.vjs-vtt-thumbnail-display') ? this.player.$('.vjs-vtt-thumbnail-display').style.width : '160px'}` + }); + + const mouseTimeDisplay = this.player.getDescendant([ + 'controlBar', + 'progressControl', + 'seekBar', + 'mouseTimeDisplay' + ]); + const timeTooltip = mouseTimeDisplay.getDescendant([ + 'timeTooltip' + ]); + + timeTooltip.el().parentElement.prepend(chapterEl); + + const getChapterFromPoint = point => { + const total = this.player.duration(); + const seekBarTime = point * total; + const chapter = Array.from(this.chaptersTrack.cues).find(marker => { + return seekBarTime >= marker.startTime && seekBarTime <= marker.endTime; + }); + return chapter ? chapter.text : ''; + }; + + timeTooltip.update = function (seekBarRect, seekBarPoint, content) { + const originalUpdateFn = Object.getPrototypeOf(this).update; + originalUpdateFn.call(this, seekBarRect, seekBarPoint, content); + chapterEl.innerHTML = getChapterFromPoint(seekBarPoint); + }; + + // Handle case of no seek-thumbnails + if (typeof this.player.vttThumbnails !== 'object') { + mouseTimeDisplay.update = function (seekBarRect, seekBarPoint) { + const time = seekBarPoint * this.player_.duration(); + const width = seekBarRect.width; + const size = chapterEl.clientWidth / 2; + + timeTooltip.updateTime(seekBarRect, seekBarPoint, time, () => { + // Make sure it doesn't exit the player + if ((seekBarRect.width * seekBarPoint) < size) { + this.el_.style.left = `${size}px`; + } else if ((seekBarRect.width * seekBarPoint) + size > width) { + this.el_.style.left = `${seekBarRect.width - size}px`; + } else { + this.el_.style.left = `${seekBarRect.width * seekBarPoint}px`; + } + }); + timeTooltip.write(videojs.time.formatTime(time)); + }; + } + }; + + return ChaptersPlugin; +}()); + +export default chapters; diff --git a/src/plugins/chapters/index.js b/src/plugins/chapters/index.js index 7b9add26..42858aaf 100644 --- a/src/plugins/chapters/index.js +++ b/src/plugins/chapters/index.js @@ -1,251 +1,9 @@ -import videojs from 'video.js'; - -import './chapters.scss'; -import { extendCloudinaryConfig, getCloudinaryUrl } from '../cloudinary/common'; - -/** - * Function to invoke when the player is ready. - * - * This is a great place for your plugin to initialize itself. When this - * function is called, the player will have its DOM and child components - * in place. - * - * @function onPlayerReady - * @param {Player} player - * A Video.js player object. - * - * @param {Object} [options={}] - * A plain object containing options for the plugin. - */ -const onPlayerReady = function onPlayerReady(player, options) { - player.addClass('vjs-chapters'); - player.chapters = new ChaptersPlugin(player, options); -}; - -/** - * A video.js plugin. - * - * In the plugin function, the value of `this` is a video.js `Player` - * instance. You cannot rely on the player being in a "ready" state here, - * depending on how the plugin is invoked. This may or may not be important - * to you; if not, remove the wait for "ready"! - * - * @function chapters - * @param {Object} [options={}] - * An object of options left to the plugin author to define. - */ -const chapters = function chapters(options) { - this.ready(function () { - onPlayerReady(this, options); - }); -}; - -/** - * Chapters - */ -const ChaptersPlugin = (function () { - - /** - * Plugin class constructor, called by videojs on - * ready event. - * - * @function constructor - * @param {Player} player - * A Video.js player object. - * - * @param {Object} [options={}] - * A plain object containing options for the plugin. - */ - function ChaptersPlugin(player, options) { - this.player = player; - this.options = options; - this.player.one('loadedmetadata', this.initializeChapters.bind(this)); - return this; +export default async function lazyChaptersPlugin(options) { + const player = this; + try { + const { default: initPlugin } = await import(/* webpackChunkName: "chapters" */ './chapters'); + player.ready(() => initPlugin(options, player)); + } catch (error) { + console.error('Failed to load plugin:', error); } - - ChaptersPlugin.prototype.src = function src(options) { - this.resetPlugin(); - this.options = options; - this.player.one('loadedmetadata', this.initializeChapters.bind(this)); - }; - - ChaptersPlugin.prototype.detach = function detach() { - this.resetPlugin(); - }; - - ChaptersPlugin.prototype.resetPlugin = function resetPlugin() { - if (this.chaptersTrack) { - this.player.$('.vjs-control-bar-chapter-display').remove(); - this.player.$('.vjs-chapter-display').remove(); - this.player.$$('.vjs-chapter-marker').forEach((el) => el.remove()); - this.player.removeRemoteTextTrack(this.chaptersTrack); - delete this.chaptersTrack; - } - }; - - ChaptersPlugin.prototype.getChaptersFileUrlByName = function getChaptersFileUrlByName() { - const currentPublicId = this.player.cloudinary.currentPublicId(); - - if (!currentPublicId) { - return null; - } - - const fullUrl = getCloudinaryUrl( - `${currentPublicId}-chapters.vtt`, - extendCloudinaryConfig(this.player.cloudinary.cloudinaryConfig(), { resource_type: 'raw', version: '1' }), - ); - return `${fullUrl}?t=${Date.now()}`; - }; - - /** - * Bootstrap the plugin. - */ - ChaptersPlugin.prototype.initializeChapters = function initializeChapters() { - const chaptersUrl = this.options === true ? this.getChaptersFileUrlByName() : this.options.url; - - if (chaptersUrl) { - // Fetch chapters VTT from URL - const chaptersTrack = { - kind: 'chapters', - src: chaptersUrl, - default: true - }; - const textTrack = this.player.addRemoteTextTrack(chaptersTrack); - textTrack.addEventListener('load', () => { - this.chaptersTrack = textTrack.track; - this.setupChaptersDisplays(); - }); - } else if (Object.entries(this.options).length) { - // Setup chapters from options - const textTrack = this.player.addRemoteTextTrack({ - kind: 'chapters', - default: true - }); - const end = this.player.duration(); - Object.entries(this.options).forEach((entry, index, arr) => { - const cue = new VTTCue( - parseFloat(entry[0]), - parseFloat(arr[index + 1] ? arr[index + 1][0] : end), - entry[1] - ); - textTrack.track.addCue(cue); - }); - this.chaptersTrack = textTrack.track; - this.setupChaptersDisplays(); - if (this.player.controlBar.chaptersButton) { - this.player.controlBar.chaptersButton.update(); - } - } - }; - - /** - * Setup chapter displays. - */ - ChaptersPlugin.prototype.setupChaptersDisplays = function initializeChapters() { - this.setupProgressBarMarkers(); - this.setupProgressBarChapter(); - this.setupControlBarChapter(); - }; - - /** - * Setup the controlbar chapter display. - */ - ChaptersPlugin.prototype.setupControlBarChapter = function setupControlBarChapter() { - const controlBarChapterHolder = - this.player.$('.vjs-control-bar-chapter-display') || document.createElement('div'); - controlBarChapterHolder.setAttribute('class', 'vjs-control-bar-chapter-display'); - - const wrapper = this.player.$('.vjs-control-bar .vjs-spacer'); - wrapper.innerHTML = ''; - wrapper.classList.add('vjs-control-bar-chapter-wrapper'); - wrapper.appendChild(controlBarChapterHolder); - - this.chaptersTrack.addEventListener('cuechange', () => { - controlBarChapterHolder.innerHTML = - this.chaptersTrack.activeCues_.length > 0 ? this.chaptersTrack.activeCues_[0].text : ''; - }); - }; - - /** - * Setup the progress bar markers. - */ - ChaptersPlugin.prototype.setupProgressBarMarkers = function setupProgressBarMarkers() { - const total = this.player.duration(); - const { seekBar } = this.player.controlBar.progressControl; - this.chaptersTrack.cues_.forEach(marker => { - if (marker.startTime !== 0) { - const markerTime = marker.startTime; - const left = (markerTime / total) * 100 + '%'; - - const markerEl = videojs.dom.createEl('div', undefined, { - class: 'vjs-chapter-marker', - style: `left: ${left}` - }); - - seekBar.el().append(markerEl); - } - }); - }; - - /** - * Setup the progrees bar on-hover chapter display. - */ - ChaptersPlugin.prototype.setupProgressBarChapter = function setupProgressBarChapter() { - const chapterEl = videojs.dom.createEl('div', undefined, { - class: 'vjs-chapter-display', - style: `max-width: ${this.player.$('.vjs-vtt-thumbnail-display') ? this.player.$('.vjs-vtt-thumbnail-display').style.width : '160px'}` - }); - - const mouseTimeDisplay = this.player.getDescendant([ - 'controlBar', - 'progressControl', - 'seekBar', - 'mouseTimeDisplay' - ]); - const timeTooltip = mouseTimeDisplay.getDescendant([ - 'timeTooltip' - ]); - - timeTooltip.el().parentElement.prepend(chapterEl); - - const getChapterFromPoint = point => { - const total = this.player.duration(); - const seekBarTime = point * total; - const chapter = this.chaptersTrack.cues_.find(marker => { - return seekBarTime >= marker.startTime && seekBarTime <= marker.endTime; - }); - return chapter ? chapter.text : ''; - }; - - timeTooltip.update = function (seekBarRect, seekBarPoint, content) { - const originalUpdateFn = Object.getPrototypeOf(this).update; - originalUpdateFn.call(this, seekBarRect, seekBarPoint, content); - chapterEl.innerHTML = getChapterFromPoint(seekBarPoint); - }; - - // Handle case of no seek-thumbnails - if (typeof this.player.vttThumbnails !== 'object') { - mouseTimeDisplay.update = function (seekBarRect, seekBarPoint) { - const time = seekBarPoint * this.player_.duration(); - const width = seekBarRect.width; - const size = chapterEl.clientWidth / 2; - - timeTooltip.updateTime(seekBarRect, seekBarPoint, time, () => { - // Make sure it doesn't exit the player - if ((seekBarRect.width * seekBarPoint) < size) { - this.el_.style.left = `${size}px`; - } else if ((seekBarRect.width * seekBarPoint) + size > width) { - this.el_.style.left = `${seekBarRect.width - size}px`; - } else { - this.el_.style.left = `${seekBarRect.width * seekBarPoint}px`; - } - }); - timeTooltip.write(videojs.time.formatTime(time)); - }; - } - }; - - return ChaptersPlugin; -}()); - -export default chapters; +} diff --git a/src/plugins/cloudinary/index.js b/src/plugins/cloudinary/index.js index b39e7e64..635db8f3 100644 --- a/src/plugins/cloudinary/index.js +++ b/src/plugins/cloudinary/index.js @@ -1,11 +1,9 @@ import videojs from 'video.js'; -import { mixin } from 'utils/mixin'; import { applyWithProps } from 'utils/apply-with-props'; import { sliceAndUnsetProperties } from 'utils/slicing'; import { isKeyInTransformation } from 'utils/cloudinary'; import { assign } from 'utils/assign'; import { isFunction } from 'utils/type-inference'; -import Playlistable from 'mixins/playlistable'; import plugins from 'plugins'; import { normalizeOptions, @@ -18,6 +16,8 @@ import VideoSource from './models/video-source/video-source'; import EventHandlerRegistry from './event-handler-registry'; import AudioSource from './models/audio-source/audio-source'; +import recommendationsOverlay from 'components/recommendations-overlay'; + const DEFAULT_PARAMS = { transformation: {}, sourceTypes: [], @@ -28,11 +28,8 @@ const DEFAULT_PARAMS = { export const CONSTRUCTOR_PARAMS = ['cloudinaryConfig', 'transformation', 'sourceTypes', 'sourceTransformation', 'posterOptions', 'autoShowRecommendations']; -class CloudinaryContext extends mixin(Playlistable) { - +class CloudinaryContext { constructor(player, options = {}) { - super(player, options); - setupCloudinaryMiddleware(); this.player = player; @@ -77,7 +74,11 @@ class CloudinaryContext extends mixin(Playlistable) { if (options.recommendationOptions) { ({ disableAutoShow, itemBuilder } = sliceAndUnsetProperties(options.recommendationOptions, 'disableAutoShow', 'itemBuilder')); } - setRecommendations(recommendations, { disableAutoShow, itemBuilder }); + + recommendationsOverlay(player).then(() => { + setRecommendations(recommendations, { disableAutoShow, itemBuilder }); + }); + } else { unsetRecommendations(); } @@ -191,7 +192,7 @@ class CloudinaryContext extends mixin(Playlistable) { }; this.dispose = () => { - if (this.playlist()) { + if (this.playlist && this.playlist()) { this.disposePlaylist(); } unsetRecommendations(); @@ -230,7 +231,7 @@ class CloudinaryContext extends mixin(Playlistable) { } }; - this.one('cldsourcechanged', _recommendations.sourceChangedHandler); + _recommendations.sourceChangedHandler(); _recommendations.endedHandler = () => { if (!disableAutoShow && this.autoShowRecommendations()) { @@ -286,7 +287,9 @@ class CloudinaryContext extends mixin(Playlistable) { this.player.src(_sources); _lastSource = src; - _lastPlaylist = this.playlist(); + if (this.playlist) { + _lastPlaylist = this.playlist(); + } }; const testCanPlayTypeAndTypeSupported = (codec) => { const v = document.createElement('video'); @@ -328,7 +331,7 @@ class CloudinaryContext extends mixin(Playlistable) { // If plugin state doesn't have an active VideoSource if (!this.source()) { // We might have been running a playlist, reset playlist's state. - if (_lastPlaylist) { + if (this.playlist && _lastPlaylist) { this.playlist(_lastPlaylist); } // Rebuild last source state without calling vjs's 'src' and 'poster' @@ -365,7 +368,7 @@ class CloudinaryContext extends mixin(Playlistable) { } } -export default function(options = {}) { +export default function (options = {}) { options.chainTarget = options.chainTarget || this; this.cloudinary = new CloudinaryContext(this, options); } diff --git a/src/plugins/cloudinary/models/video-source/video-source.const.js b/src/plugins/cloudinary/models/video-source/video-source.const.js index ea71ebec..11b345fa 100644 --- a/src/plugins/cloudinary/models/video-source/video-source.const.js +++ b/src/plugins/cloudinary/models/video-source/video-source.const.js @@ -1,20 +1,38 @@ export const DEFAULT_POSTER_PARAMS = { format: 'jpg', resource_type: 'video' }; -const DEFAULT_VIDEO_SOURCE_TYPES = ['webm/vp9', 'mp4/h265', 'mp4']; - export const DEFAULT_VIDEO_PARAMS = { resource_type: 'video', type: 'upload', transformation: [], sourceTransformation: {}, - sourceTypes: DEFAULT_VIDEO_SOURCE_TYPES, + sourceTypes: ['auto'], recommendations: null, info: {}, interactionAreas: {}, chapters: {} }; -export const VIDEO_SUFFIX_REMOVAL_PATTERN = RegExp(`\\.(${DEFAULT_VIDEO_SOURCE_TYPES.join('|')})$$`); +const COMMON_VIDEO_EXTENSIONS = [ + '3g2', + '3gp', + 'avi', + 'flv', + 'm3u8', + 'ts', + 'm2ts', + 'mts', + 'mov', + 'mkv', + 'mp4', + 'mpeg', + 'mpd', + 'mxf', + 'ogv', + 'webm', + 'wmv' +]; // https://cloudinary.com/documentation/video_manipulation_and_delivery#supported_video_formats + +export const VIDEO_SUFFIX_REMOVAL_PATTERN = RegExp(`\\.(${COMMON_VIDEO_EXTENSIONS.join('|')})$$`); // eslint-disable-next-line no-control-regex export const URL_PATTERN = RegExp('https?:\\/\\/(www\\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b([-a-zA-Z0-9()@:%_\+.~#?&/=]*)'); diff --git a/src/plugins/cloudinary/models/video-source/video-source.js b/src/plugins/cloudinary/models/video-source/video-source.js index 8f81b79e..e4a30db3 100644 --- a/src/plugins/cloudinary/models/video-source/video-source.js +++ b/src/plugins/cloudinary/models/video-source/video-source.js @@ -1,4 +1,3 @@ -import videojs from 'video.js'; import { sliceAndUnsetProperties } from 'utils/slicing'; import { assign } from 'utils/assign'; import { objectToQuerystring } from 'utils/querystring'; @@ -187,7 +186,7 @@ class VideoSource extends BaseSource { generateSources() { if (this.isRawUrl) { - const type = this.sourceTypes().length > 1 ? null : this.sourceTypes()[0]; + const type = this.sourceTypes()[0] === 'auto' ? null : this.sourceTypes()[0]; return [this.generateRawSource(this.publicId(), type)]; } @@ -241,12 +240,7 @@ class VideoSource extends BaseSource { }; }); - if (videojs.browser.IS_ANY_SAFARI) { - // filter out dash on safari - return srcs.filter(s => s.type.indexOf('application/dash+xml') === -1); - } else { - return srcs; - } + return srcs; } generateRawSource(url, type) { diff --git a/src/plugins/ima/ima.js b/src/plugins/ima/ima.js index a6a4286d..1b8b0f6a 100644 --- a/src/plugins/ima/ima.js +++ b/src/plugins/ima/ima.js @@ -1,1775 +1,3 @@ -/* eslint-disable */ -/** - * Copyright 2014 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * IMA SDK integration plugin for Video.js. For more information see - * https://www.github.com/googleads/videojs-ima - */ - -(function(factory) { - if (typeof define === 'function' && define['amd']) { - define(['video.js', 'videojs-contrib-ads'], function(videojs){ factory(window, document, (videojs.default || videojs)) }); - } else if (typeof exports === 'object' && typeof module === 'object') { - var vjs = require('video.js'); - require('videojs-contrib-ads'); - factory(window, document, vjs); - } else { - factory(window, document, videojs); - } -})(function(window, document, videojs) { - "use strict"; - - var extend = function(obj) { - var arg; - var index; - var key; - for (index = 1; index < arguments.length; index++) { - arg = arguments[index]; - for (key in arg) { - if (arg.hasOwnProperty(key)) { - obj[key] = arg[key]; - } - } - } - return obj; - }; - - var ima_defaults = { - debug: false, - timeout: 5000, - prerollTimeout: 100, - adLabel: 'Advertisement', - showControlsForAds: true, - showControlsForJSAds: true, - adsRenderingSettings: { - uiElements: [], - }, - }; - - var eventTypes = (videojs.browser.IS_ANDROID || videojs.browser.IS_IOS) ? { - click: 'touchend', - mousedown: 'touchstart', - mouseup: 'touchend', - mousemove: 'touchmove' - } : { - click: 'click', - mousedown: 'mousedown', - mouseup: 'mouseup', - mousemove: 'mousemove' - }; - - var init = function(options, readyCallback) { - this.ima = new ImaPlugin(this, options, readyCallback); - }; - - var ImaPlugin = function(player, options, readyCallback) { - this.player = player; - - /** - * Assigns the unique id and class names to the given element as well as the style class - * @param element - * @param controlName - * @private - */ - var assignControlAttributes_ = function(element, controlName) { - element.id = this.controlPrefix + controlName; - element.className = this.controlPrefix + controlName + ' ' + controlName; - }.bind(this); - - /** - * Returns a regular expression to test a string for the given className - * @param className - * @returns {RegExp} - * @private - */ - var getClassRegexp_ = function(className){ - // Matches on - // (beginning of string OR NOT word char) - // classname - // (negative lookahead word char OR end of string) - return new RegExp('(^|[^A-Za-z-])' + className + '((?![A-Za-z-])|$)', 'gi'); - }; - - /** - * Adds a class to the given element if it doesn't already have the class - * @param element - * @param classToAdd - * @private - */ - var addClass_ = function(element, classToAdd){ - if(getClassRegexp_(classToAdd).test(element.className)){ - return element; - } - - return element.className = element.className.trim() + ' ' + classToAdd; - }; - - /** - * Removes a class from the given element if it has the given class - * @param element - * @param classToRemove - * @private - */ - var removeClass_ = function(element, classToRemove){ - var classRegexp = getClassRegexp_(classToRemove); - - if(!classRegexp.test(element.className)){ - return element; - } - - return element.className = element.className.trim().replace(classRegexp, ''); - }; - - /** - * Creates the ad container passed to the IMA SDK. - * @private - */ - var createAdContainer_ = function() { - // The adContainerDiv is the DOM of the element that will house - // the ads and ad controls. - this.vjsControls = this.player.getChild('controlBar'); - this.adContainerDiv = - this.vjsControls.el().parentNode.appendChild( - document.createElement('div')); - assignControlAttributes_(this.adContainerDiv, 'ima-ad-container'); - this.adContainerDiv.style.position = "absolute"; - this.adContainerDiv.addEventListener( - 'mouseenter', - showAdControls_, - false); - this.adContainerDiv.addEventListener( - 'mouseleave', - hideAdControls_, - false); - createControls_(); - this.adDisplayContainer = - new google.ima.AdDisplayContainer(this.adContainerDiv, this.contentPlayer); - this.showAdContainer(!this.settings.manual); - }.bind(this); - - /** - * Creates the controls for the ad. - * @private - */ - var createControls_ = function() { - this.controlsDiv = document.createElement('div'); - assignControlAttributes_(this.controlsDiv, 'ima-controls-div'); - this.controlsDiv.style.width = '100%'; - this.countdownDiv = document.createElement('div'); - assignControlAttributes_(this.countdownDiv, 'ima-countdown-div'); - this.countdownDiv.innerHTML = this.settings.adLabel; - this.countdownDiv.style.display = this.showCountdown ? '' : 'none'; - this.seekBarDiv = document.createElement('div'); - assignControlAttributes_(this.seekBarDiv, 'ima-seek-bar-div'); - this.seekBarDiv.style.width = '100%'; - this.progressDiv = document.createElement('div'); - assignControlAttributes_(this.progressDiv, 'ima-progress-div'); - this.playPauseDiv = document.createElement('div'); - assignControlAttributes_(this.playPauseDiv, 'ima-play-pause-div'); - addClass_(this.playPauseDiv, 'ima-playing'); - this.playPauseDiv.addEventListener( - eventTypes.click, - onAdPlayPauseClick_, - false); - this.muteDiv = document.createElement('div'); - assignControlAttributes_(this.muteDiv, 'ima-mute-div'); - addClass_(this.muteDiv, 'ima-non-muted'); - this.muteDiv.addEventListener( - eventTypes.click, - onAdMuteClick_, - false); - this.sliderDiv = document.createElement('div'); - assignControlAttributes_(this.sliderDiv, 'ima-slider-div'); - this.sliderDiv.addEventListener( - eventTypes.mousedown, - onAdVolumeSliderMouseDown_, - false); - this.sliderLevelDiv = document.createElement('div'); - assignControlAttributes_(this.sliderLevelDiv, 'ima-slider-level-div'); - this.fullscreenDiv = document.createElement('div'); - assignControlAttributes_(this.fullscreenDiv, 'ima-fullscreen-div'); - addClass_(this.fullscreenDiv, 'ima-non-fullscreen'); - this.fullscreenDiv.addEventListener( - eventTypes.click, - onAdFullscreenClick_, - false); - this.adContainerDiv.appendChild(this.controlsDiv); - this.controlsDiv.appendChild(this.seekBarDiv); - this.controlsDiv.appendChild(this.playPauseDiv); - this.controlsDiv.appendChild(this.muteDiv); - this.controlsDiv.appendChild(this.sliderDiv); - this.controlsDiv.appendChild(this.fullscreenDiv); - this.seekBarDiv.appendChild(this.progressDiv); - this.sliderDiv.appendChild(this.sliderLevelDiv); - if (this.settings.vjsControls) { - this.initVjsControls(); - this.controlsDiv.style.display = 'none'; - this.vjsControls.el().appendChild(this.countdownDiv); - } else { - this.controlsDiv.appendChild(this.countdownDiv); - } - }.bind(this); - - this.showAdContainer = function(show) { - this.adContainerDiv.style.display = show ? 'block' : 'none'; - this.player.toggleClass('vjs-ima-ad', show); - }.bind(this); - - /** - * Initializes the AdDisplayContainer. On mobile, this must be done as a - * result of user action. - */ - this.initializeAdDisplayContainer = function() { - this.adDisplayContainerInitialized = true; - this.adDisplayContainer.initialize(); - }.bind(this); - - /** - * Creates the AdsRequest and request ads through the AdsLoader. - */ - this.requestAds = function() { - if (!this.adDisplayContainerInitialized) { - this.adDisplayContainer.initialize(); - } - var adsRequest = new google.ima.AdsRequest(); - if (this.settings.adTagUrl) { - adsRequest.adTagUrl = this.settings.adTagUrl; - } else { - adsRequest.adsResponse = this.settings.adsResponse; - } - if (this.settings.forceNonLinearFullSlot) { - adsRequest.forceNonLinearFullSlot = true; - } - - adsRequest.linearAdSlotWidth = this.getPlayerWidth(); - adsRequest.linearAdSlotHeight = this.getPlayerHeight(); - adsRequest.nonLinearAdSlotWidth = - this.settings.nonLinearWidth || this.getPlayerWidth(); - adsRequest.nonLinearAdSlotHeight = - this.settings.nonLinearHeight || (this.getPlayerHeight() / 3); - adsRequest.vastLoadTimeout = Math.max(this.settings.prerollTimeout, - this.settings.postrollTimeout); - - this.adsLoader.requestAds(adsRequest); - }.bind(this); - - /** - * Listener for the ADS_MANAGER_LOADED event. Creates the AdsManager, - * sets up event listeners, and triggers the 'adsready' event for - * videojs-ads-contrib. - * @private - */ - var onAdsManagerLoaded_ = function(adsManagerLoadedEvent) { - this.adsManager = adsManagerLoadedEvent.getAdsManager( - this.contentPlayheadTracker, this.adsRenderingSettings); - - this.adsManager.addEventListener( - google.ima.AdErrorEvent.Type.AD_ERROR, - onAdError_); - this.adsManager.addEventListener( - google.ima.AdEvent.Type.AD_BREAK_READY, - onAdBreakReady_); - this.adsManager.addEventListener( - google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED, - this.onContentPauseRequested_); - this.adsManager.addEventListener( - google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED, - this.onContentResumeRequested_); - this.adsManager.addEventListener( - google.ima.AdEvent.Type.ALL_ADS_COMPLETED, - onAllAdsCompleted_); - - this.adsManager.addEventListener( - google.ima.AdEvent.Type.LOADED, - onAdLoaded_); - this.adsManager.addEventListener( - google.ima.AdEvent.Type.STARTED, - onAdStarted_); - this.adsManager.addEventListener( - google.ima.AdEvent.Type.CLICK, - onAdPlayPauseClick_); - this.adsManager.addEventListener( - google.ima.AdEvent.Type.COMPLETE, - this.onAdComplete_); - this.adsManager.addEventListener( - google.ima.AdEvent.Type.SKIPPED, - this.onAdComplete_); - this.adsManager.addEventListener( - google.ima.AdEvent.Type.PAUSED, - this.onAdPaused_); - this.adsManager.addEventListener( - google.ima.AdEvent.Type.RESUMED, - this.onAdResumed_); - - var eventsMap = { - 'load': google.ima.AdEvent.Type.LOADED, - 'ad-started': google.ima.AdEvent.Type.STARTED, - 'click': google.ima.AdEvent.Type.CLICK, - 'ad-ended': google.ima.AdEvent.Type.COMPLETE, - 'ad-skipped': google.ima.AdEvent.Type.SKIPPED, - 'first-quartile': google.ima.AdEvent.Type.FIRST_QUARTILE, - 'midpoint': google.ima.AdEvent.Type.MIDPOINT, - 'third-quartile': google.ima.AdEvent.Type.THIRD_QUARTILE, - 'impression': google.ima.AdEvent.Type.IMPRESSION, - 'pause': google.ima.AdEvent.Type.PAUSED, - 'play': google.ima.AdEvent.Type.RESUMED, - 'mute': google.ima.AdEvent.Type.VOLUME_MUTED, - 'allpods-completed': google.ima.AdEvent.Type.ALL_ADS_COMPLETED - }; - - Object.keys(eventsMap).forEach(function(event){ - this.adsManager.addEventListener(eventsMap[event], function(){ - this.player.trigger('ads-'+event); - }.bind(this)); - - }.bind(this)); - - setAdMuted(this.player.muted()); - - if (!this.autoPlayAdBreaks) { - try { - var initWidth = this.getPlayerWidth(); - var initHeight = this.getPlayerHeight(); - this.adsManagerDimensions.width = initWidth; - this.adsManagerDimensions.height = initHeight; - this.adsManager.init( - initWidth, - initHeight, - google.ima.ViewMode.NORMAL); - this.adsManager.setVolume(this.player.muted() ? 0 : this.player.volume()); - } catch (adError) { - onAdError_(adError); - } - } - - var cuepoints = this.adsManager.getCuePoints(); - var foundpreroll = !cuepoints.length; // no playlist, just preroll - var foundpostroll = false;; - cuepoints.forEach(function(offset){ - if (!offset) - foundpreroll = true; - else if (offset==-1) - foundpostroll = true; - }); - if (!foundpreroll) - this.player.trigger('nopreroll'); - if (!foundpostroll) - this.player.trigger('nopostroll'); - if (cuepoints.length) - this.player.trigger('ads-cuepoints', cuepoints); - - this.player.trigger('adsready'); - }.bind(this); - - /** - * DEPRECATED: Use startFromReadyCallback - * Start ad playback, or content video playback in the absence of a - * pre-roll. - */ - this.start = function() { - window.console.log( - 'WARNING: player.ima.start is deprecated. Use ' + - 'player.ima.startFromReadyCallback instead.'); - }; - - /** - * Start ad playback, or content video playback in the absence of a - * pre-roll. **NOTE**: This method only needs to be called if you provide - * your own readyCallback as the second parameter to player.ima(). If you - * only provide options and do not provide your own readyCallback, - * **DO NOT** call this method. If you do provide your own readyCallback, - * you should call this method in the last line of that callback. For more - * info, see this method's usage in our advanced and playlist examples. - */ - this.startFromReadyCallback = function() { - if (this.autoPlayAdBreaks && this.adsManager) { - try { - this.adsManager.init( - this.getPlayerWidth(), - this.getPlayerHeight(), - google.ima.ViewMode.NORMAL); - this.adsManager.setVolume(this.player.muted() ? 0 : this.player.volume()); - this.adsManager.start(); - } catch (adError) { - onAdError_(adError); - } - } - }.bind(this); - - /** - * Listener for errors fired by the AdsLoader. - * @param {google.ima.AdErrorEvent} event The error event thrown by the - * AdsLoader. See - * https://developers.google.com/interactive-media-ads/docs/sdks/html5/v3/apis#ima.AdError.Type - * @private - */ - var onAdsLoaderError_ = function(event) { - console.log('AdsLoader error: ' + event.getError()); - this.showAdContainer(false); - if (this.adsManager) { - this.adsManager.destroy(); - } - this.player.trigger({type: 'adserror', data: { AdError: event.getError(), AdErrorEvent: event }}); - }.bind(this); - - /** - * Listener for errors thrown by the AdsManager. - * @param {google.ima.AdErrorEvent} adErrorEvent The error event thrown by - * the AdsManager. - * @private - */ - var onAdError_ = function(adErrorEvent) { - var errorMessage = adErrorEvent.getError !== undefined ? adErrorEvent.getError() : adErrorEvent.stack; - console.log('Ad error: ' + errorMessage); - this.adsActive = false; - this.adPlaying = false; - this.restoreLoop(); - this.vjsControls.show(); - this.adsManager.destroy(); - this.showAdContainer(false); - this.updateVjsControls(); - this.player.trigger({ type: 'adserror', data: { AdError: errorMessage, AdErrorEvent: adErrorEvent }}); - }.bind(this); - - /** - * Listener for AD_BREAK_READY. Passes event on to publisher's listener. - * @param {google.ima.AdEvent} adEvent AdEvent thrown by the AdsManager. - * @private - */ - var onAdBreakReady_ = function(adEvent) { - this.adBreakReadyListener(adEvent); - }.bind(this); - - /** - * Called by publishers in manual ad break playback mode to start an ad - * break. - */ - this.playAdBreak = function() { - if (!this.autoPlayAdBreaks) { - this.adsManager.start(); - } - }.bind(this); - - this.resetLoop = function() { - this.contentLoop = this.contentPlayer && this.contentPlayer.loop; - if (this.contentLoop) { - this.contentPlayer.loop = false; - } - }.bind(this); - - this.restoreLoop = function() { - if (this.contentLoop) { - this.contentPlayer.loop = true; - this.contentLoop = false; - } - }.bind(this); - - /** - * Pauses the content video and displays the ad container so ads can play. - * @param {google.ima.AdEvent} adEvent The AdEvent thrown by the AdsManager. - * @private - */ - this.onContentPauseRequested_ = function(adEvent) { - this.contentSource = this.player.currentSrc(); - this.resetLoop(); - this.player.off('contentended', this.localContentEndedListener); - this.player.ads.startLinearAdMode(); - this.showAdContainer(true); - - var contentType = adEvent.getAd().getContentType(); - if (!this.settings.vjsControls || !this.settings.showControlsForAds){ - if (!this.settings.showControlsForAds - || ((contentType === 'application/javascript') && !this.settings.showControlsForJSAds)) { - this.controlsDiv.style.display = 'none'; - } else { - this.controlsDiv.style.display = 'block'; - } - this.vjsControls.hide(); - } - showPlayButton(); - this.player.pause(); - this.adsActive = true; - this.adPlaying = true; - this.updateVjsControls(); - }.bind(this); - - /** - * Resumes content video and hides the ad container. - * @param {google.ima.AdEvent} adEvent The AdEvent thrown by the AdsManager. - * @private - */ - this.onContentResumeRequested_ = function(adEvent) { - this.contentResumeTimer = clearTimeout(this.contentResumeTimer); - this.restoreLoop(); - this.adsActive = false; - this.adPlaying = false; - this.player.on('contentended', this.localContentEndedListener); - if (this.currentAd == null || // hide for post-roll only playlist - this.currentAd.isLinear()) { // don't hide for non-linear ads - this.showAdContainer(false); - } - this.vjsControls.show(); - this.player.ads.endLinearAdMode(); - this.countdownDiv.innerHTML = ''; - this.updateVjsControls(); - }.bind(this); - - /** - * Records that ads have completed and calls contentAndAdsEndedListeners - * if content is also complete. - * @param {google.ima.AdEvent} adEvent The AdEvent thrown by the AdsManager. - * @private - */ - var onAllAdsCompleted_ = function(adEvent) { - this.allAdsCompleted = true; - this.showAdContainer(false); - if (this.contentComplete == true) { - if (this.contentPlayer.src && !/^blob:/.test(this.contentPlayer.src) && - this.contentSource && this.contentPlayer.src != this.contentSource) { - this.player.src(this.contentSource); - } - this.player.trigger('') - for (var index in this.contentAndAdsEndedListeners) { - this.contentAndAdsEndedListeners[index](); - } - } - }.bind(this); - - /** - * Starts the content video when a non-linear ad is loaded. - * @param {google.ima.AdEvent} adEvent The AdEvent thrown by the AdsManager. - * @private - */ - var onAdLoaded_ = function(adEvent) { - if (!adEvent.getAd().isLinear() && !this.player.ended()) { - this.player.ads.endLinearAdMode(); - this.player.play(); - } - }.bind(this); - - /** - * Starts the interval timer to check the current ad time when an ad starts - * playing. - * @param {google.ima.AdEvent} adEvent The AdEvent thrown by the AdsManager. - * @private - */ - var onAdStarted_ = function(adEvent) { - this.currentAd = adEvent.getAd(); - if (this.currentAd.isLinear()) { - this.adTrackingTimer = setInterval( - onAdPlayheadTrackerInterval_, 250); - // Don't bump container when controls are shown - removeClass_(this.adContainerDiv, 'bumpable-ima-ad-container'); - } else { - // Bump container when controls are shown - addClass_(this.adContainerDiv, 'bumpable-ima-ad-container'); - this.player.addClass('vjs-ima-non-linear'); - this.showAdContainer(true); - } - }.bind(this); - - /** - * Clears the interval timer for current ad time when an ad completes. - * @param {google.ima.AdEvent} adEvent The AdEvent thrown by the AdsManager. - * @private - */ - this.onAdComplete_ = function(adEvent) { - if (this.currentAd.isLinear()) { - clearInterval(this.adTrackingTimer); - var pod = this.currentAd.getAdPodInfo(); - if (pod && pod.getAdPosition() < pod.getTotalAds()) { - this.player.trigger('ads-pod-ended') - return; - } - - // this is the final ad so we excpect ima sdk to trigger - // CONTENT_RESUME_REQUESTED, but for some reason it isn't triggered - // reliably on iOS, so we fake it - - this.contentResumeTimer = setTimeout(function(){ - this.onContentResumeRequested_(null); - }.bind(this), 1000); - } else { - this.player.removeClass('vjs-ima-non-linear'); - } - }.bind(this); - - this.onAdPaused_ = function(adEvent) { - showPauseButton(); - this.adPlaying = false; - }.bind(this); - - this.onAdResumed_ = function(adEvent) { - showPlayButton(); - this.adPlaying = true; - }.bind(this); - - var formatTime = function(time) { - var m = Math.floor(time / 60); - var s = Math.floor(time % 60); - if (s.toString().length < 2) { - s = '0' + s; - } - return m + ':' + s; - }; - - /** - * Gets the current time and duration of the ad and calls the method to - * update the ad UI. - * @private - */ - var onAdPlayheadTrackerInterval_ = function() { - var remainingTime = this.adsManager.getRemainingTime(); - var duration = this.currentAd.getDuration(); - var currentTime = duration - remainingTime; - currentTime = currentTime > 0 ? currentTime : 0; - var isPod = false; - var totalAds = 0; - var adPosition; - if (this.currentAd.getAdPodInfo()) { - isPod = true; - adPosition = this.currentAd.getAdPodInfo().getAdPosition(); - totalAds = this.currentAd.getAdPodInfo().getTotalAds(); - } - - // Update countdown timer data - var podCount = ': '; - if (isPod && (totalAds > 1)) { - podCount = ' (' + adPosition + ' of ' + totalAds + '): '; - } - this.countdownDiv.innerHTML = - this.settings.adLabel + podCount + formatTime(remainingTime); - - // Update UI - var playProgressRatio = currentTime / duration; - var playProgressPercent = playProgressRatio * 100; - this.progressDiv.style.width = playProgressPercent + '%'; - this.updateVjsControls(); - }.bind(this); - - this.getPlayerWidth = function() { - var retVal = parseInt(getComputedStyle(this.player.el()).width, 10) || - this.player.width(); - return retVal; - }.bind(this); - - this.getPlayerHeight = function() { - var retVal = parseInt(getComputedStyle(this.player.el()).height, 10) || - this.player.height(); - return retVal; - }.bind(this); - - /** - * Hides the ad controls on mouseout. - * @private - */ - var hideAdControls_ = function() { - this.controlsDiv.style.height = '14px'; - this.playPauseDiv.style.display = 'none'; - this.muteDiv.style.display = 'none'; - this.sliderDiv.style.display = 'none'; - this.fullscreenDiv.style.display = 'none'; - }.bind(this); - - /** - * Shows ad controls on mouseover. - * @private - */ - var showAdControls_ = function() { - this.controlsDiv.style.height = '37px'; - this.playPauseDiv.style.display = 'block'; - this.muteDiv.style.display = 'block'; - this.sliderDiv.style.display = 'block'; - this.fullscreenDiv.style.display = 'block'; - }.bind(this); - - /** - * Show pause and hide play button - */ - var showPauseButton = function() { - addClass_(this.playPauseDiv, 'ima-paused'); - removeClass_(this.playPauseDiv, 'ima-playing'); - }.bind(this); - - /** - * Show play and hide pause button - */ - var showPlayButton = function() { - addClass_(this.playPauseDiv, 'ima-playing'); - removeClass_(this.playPauseDiv, 'ima-paused'); - }.bind(this); - - /** - * Listener for clicks on the play/pause button during ad playback. - * @private - */ - var onAdPlayPauseClick_ = function() { - if (this.adPlaying) { - showPauseButton(); - this.adsManager.pause(); - this.adPlaying = false; - } else { - showPlayButton(); - this.adsManager.resume(); - this.adPlaying = true; - } - }.bind(this); - - /** - * Listener for clicks on the mute button during ad playback. - * @private - */ - var onAdMuteClick_ = function() { - setAdMuted(!this.adMuted); - }.bind(this); - - /* Listener for mouse down events during ad playback. Used for volume. - * @private - */ - var onAdVolumeSliderMouseDown_ = function() { - document.addEventListener(eventTypes.mouseup, onMouseUp_, false); - document.addEventListener(eventTypes.mousemove, onMouseMove_, false); - }; - - /* Mouse movement listener used for volume slider. - * @private - */ - var onMouseMove_ = function(event) { - setVolumeSlider_(event); - }; - - /* Mouse release listener used for volume slider. - * @private - */ - var onMouseUp_ = function(event) { - setVolumeSlider_(event); - document.removeEventListener(eventTypes.mousemove, onMouseMove_); - document.removeEventListener(eventTypes.mouseup, onMouseUp_); - }; - - /* Utility function to set volume and associated UI - * @private - */ - var setVolumeSlider_ = function(event) { - var clientX = event.changedTouches ? event.changedTouches[0].clientX : - event.clientX; - var percent = (clientX - this.sliderDiv.getBoundingClientRect().left) / - this.sliderDiv.offsetWidth; - percent *= 100; - //Bounds value 0-100 if mouse is outside slider region. - percent = Math.min(Math.max(percent, 0), 100); - this.sliderLevelDiv.style.width = percent + "%"; - this.player.volume(percent / 100); //0-1 - this.adsManager.setVolume(percent / 100); - if (this.player.volume() == 0) { - addClass_(this.muteDiv, 'ima-muted'); - removeClass_(this.muteDiv, 'ima-non-muted'); - this.player.muted(true); - this.adMuted = true; - } - else - { - addClass_(this.muteDiv, 'ima-non-muted'); - removeClass_(this.muteDiv, 'ima-muted'); - this.player.muted(false); - this.adMuted = false; - } - }.bind(this); - - /** - * Listener for clicks on the fullscreen button during ad playback. - * @private - */ - var onAdFullscreenClick_ = function() { - if (this.player.isFullscreen()) { - this.player.exitFullscreen(); - } else { - this.player.requestFullscreen(); - } - }.bind(this); - - /** - * Listens for the video.js player to change its fullscreen status. This - * keeps the fullscreen-ness of the AdContainer in sync with the player. - * @private - */ - var onFullscreenChange_ = function() { - if (this.player.isFullscreen()) { - addClass_(this.fullscreenDiv, 'ima-fullscreen'); - removeClass_(this.fullscreenDiv, 'ima-non-fullscreen'); - if (this.adsManager) { - this.adsManager.resize( - window.screen.width, - window.screen.height, - google.ima.ViewMode.FULLSCREEN); - } - } else { - addClass_(this.fullscreenDiv, 'ima-non-fullscreen'); - removeClass_(this.fullscreenDiv, 'ima-fullscreen'); - if (this.adsManager) { - this.adsManager.resize( - this.getPlayerWidth(), - this.getPlayerHeight(), - google.ima.ViewMode.NORMAL); - } - } - }.bind(this); - - /** - * Listens for the video.js player to change its volume. This keeps the ad - * volume in sync with the content volume if the volume of the player is - * changed while content is playing - * @private - */ - var onVolumeChange_ = function() { - var newVolume = this.player.muted() ? 0 : this.player.volume(); - if (this.adsManager) { - this.adsManager.setVolume(newVolume); - } - // Update UI - if (newVolume == 0) { - this.adMuted = true; - addClass_(this.muteDiv, 'ima-muted'); - removeClass_(this.muteDiv, 'ima-non-muted'); - this.sliderLevelDiv.style.width = '0%'; - } else { - this.adMuted = false; - addClass_(this.muteDiv, 'ima-non-muted'); - removeClass_(this.muteDiv, 'ima-muted'); - this.sliderLevelDiv.style.width = newVolume * 100 + '%'; - } - }.bind(this); - - /** - * Seeks content to 00:00:00. This is used as an event handler for the - * loadedmetadata event, since seeking is not possible until that event has - * fired. - * @private - */ - var seekContentToZero_ = function() { - this.player.off('loadedmetadata', seekContentToZero_); - this.player.currentTime(0); - }.bind(this); - - /** - * Seeks content to 00:00:00 and starts playback. This is used as an event - * handler for the loadedmetadata event, since seeking is not possible until - * that event has fired. - * @private - */ - var playContentFromZero_ = function() { - this.player.off('loadedmetadata', playContentFromZero_); - this.player.currentTime(0); - this.player.play(); - }.bind(this); - - /** - * Destroys the AdsManager, sets it to null, and calls contentComplete to - * reset correlators. Once this is done it requests ads again to keep the - * inventory available. - * @private - */ - var resetIMA_ = function() { - this.adsActive = false; - this.adPlaying = false; - this.restoreLoop(); - this.player.on('contentended', this.localContentEndedListener); - if (this.currentAd && this.currentAd.isLinear()) { - this.showAdContainer(false); - } - this.vjsControls.show(); - this.player.ads.endLinearAdMode(); - this.contentPlayheadTracker.currentTime = 0; - this.countdownDiv.innerHTML = ''; - this.updateVjsControls(); - if (this.adTrackingTimer) { - // If this is called while an ad is playing, stop trying to get that - // ad's current time. - clearInterval(this.adTrackingTimer); - } - if (this.adsManager) { - this.adsManager.destroy(); - this.adsManager = null; - } - if (this.adsLoader && !this.contentComplete) { - this.adsLoader.contentComplete(); - } - this.contentComplete = false; - this.allAdsCompleted = false; - }.bind(this); - - /** - * Ads an EventListener to the AdsManager. For a list of available events, - * see - * https://developers.google.com/interactive-media-ads/docs/sdks/html5/v3/apis#ima.AdEvent.Type - * @param {google.ima.AdEvent.Type} event The AdEvent.Type for which to listen. - * @param {function} callback The method to call when the event is fired. - */ - this.addEventListener = function(event, callback) { - if (this.adsManager) { - this.adsManager.addEventListener(event, callback); - } - }.bind(this); - - /** - * Returns the instance of the AdsManager. - * @return {google.ima.AdsManager} The AdsManager being used by the plugin. - */ - this.getAdsManager = function() { - return this.adsManager; - }.bind(this); - - /** - * DEPRECATED: Use setContentWithAdTag. - * Sets the content of the video player. You should use this method instead - * of setting the content src directly to ensure the proper ad tag is - * requested when the video content is loaded. - * @param {?string} contentSrc The URI for the content to be played. Leave - * blank to use the existing content. - * @param {?string} adTag The ad tag to be requested when the content loads. - * Leave blank to use the existing ad tag. - * @param {?boolean} playOnLoad True to play the content once it has loaded, - * false to only load the content but not start playback. - */ - this.setContent = function(contentSrc, adTag, playOnLoad) { - window.console.log( - 'WARNING: player.ima.setContent is deprecated. Use ' + - 'player.ima.setContentWithAdTag instead.'); - this.setContentWithAdTag(contentSrc, adTag, playOnLoad); - }.bind(this); - - /** - * Sets the content of the video player. You should use this method instead - * of setting the content src directly to ensure the proper ad tag is - * requested when the video content is loaded. - * @param {?string} contentSrc The URI for the content to be played. Leave - * blank to use the existing content. - * @param {?string} adTag The ad tag to be requested when the content loads. - * Leave blank to use the existing ad tag. - * @param {?boolean} playOnLoad True to play the content once it has loaded, - * false to only load the content but not start playback. - */ - this.setContentWithAdTag = function(contentSrc, adTag, playOnLoad) { - resetIMA_(); - this.settings.adTagUrl = adTag ? adTag : this.settings.adTagUrl; - changeSource_(contentSrc, playOnLoad); - }.bind(this); - - /** - * Sets the content of the video player. You should use this method instead - * of setting the content src directly to ensure the proper ads response is - * used when the video content is loaded. - * @param {?string} contentSrc The URI for the content to be played. Leave - * blank to use the existing content. - * @param {?string} adsResponse The ads response to be requested when the - * content loads. Leave blank to use the existing ads response. - * @param {?boolean} playOnLoad True to play the content once it has loaded, - * false to only load the content but not start playback. - */ - this.setContentWithAdsResponse = function(contentSrc, adsResponse, playOnLoad) { - resetIMA_(); - this.settings.adsResponse = adsResponse ? adsResponse : this.settings.adsResponse; - changeSource_(contentSrc, playOnLoad); - }.bind(this); - - /** - * Plays an ad immediately - * @param {?string} adTag The ad tag to be requested. - * Leave blank to use the existing ad tag. - */ - this.playAd = function(adTag) { - resetIMA_(); - this.settings.adTagUrl = adTag ? adTag : this.settings.adTagUrl; - // this.showAdContainer(true); - // this.vjsControls.hide(); - this.requestAds(); - }.bind(this); - - /** - * Changes the player source. - * @param {?string} contentSrc The URI for the content to be played. Leave - * blank to use the existing content. - * @param {?boolean} playOnLoad True to play the content once it has loaded, - * false to only load the content but not start playback. - * @private - */ - var changeSource_ = function(contentSrc, playOnLoad) { - // Only try to pause the player when initialised with a source already - if (!!this.player.currentSrc()) { - this.player.currentTime(0); - this.player.pause(); - } - if (contentSrc) { - this.player.src(contentSrc); - } - if (playOnLoad) { - this.player.on('loadedmetadata', playContentFromZero_); - } else { - this.player.on('loadedmetadata', seekContentToZero_); - } - }.bind(this); - - var setAdMuted = function(mute) { - if (mute) { - addClass_(this.muteDiv, 'ima-muted'); - removeClass_(this.muteDiv, 'ima-non-muted'); - this.adsManager.setVolume(0); - // Bubble down to content player - this.player.muted(true); - this.adMuted = true; - this.sliderLevelDiv.style.width = "0%"; - } else { - addClass_(this.muteDiv, 'ima-non-muted'); - removeClass_(this.muteDiv, 'ima-muted'); - this.adsManager.setVolume(this.player.volume()); - // Bubble down to content player - this.player.muted(false); - this.adMuted = false; - this.sliderLevelDiv.style.width = this.player.volume() * 100 + "%"; - } - }.bind(this); - /** - * Adds a listener for the 'contentended' event of the video player. This should be - * used instead of setting an 'contentended' listener directly to ensure that the - * ima can do proper cleanup of the SDK before other event listeners - * are called. - * @param {function} listener The listener to be called when content completes. - */ - this.addContentEndedListener = function(listener) { - this.contentEndedListeners.push(listener); - }.bind(this); - - /** - * Adds a listener that will be called when content and all ads have - * finished playing. - * @param {function} listener The listener to be called when content and ads complete. - */ - this.addContentAndAdsEndedListener = function(listener) { - this.contentAndAdsEndedListeners.push(listener); - }.bind(this); - - /** - * Sets the listener to be called to trigger manual ad break playback. - * @param {function} listener The listener to be called to trigger manual ad break playback. - */ - this.setAdBreakReadyListener = function(listener) { - this.adBreakReadyListener = listener; - }.bind(this); - - /** - * Pauses the ad. - */ - this.pauseAd = function() { - if (this.adsActive && this.adPlaying) { - showPauseButton(); - this.adsManager.pause(); - this.adPlaying = false; - } - }.bind(this); - - /** - * Resumes the ad. - */ - this.resumeAd = function() { - if (this.adsActive && !this.adPlaying) { - showPlayButton(); - this.adsManager.resume(); - this.adPlaying = true; - } - }.bind(this); - - /** - * Set up intervals to check for seeking and update current video time. - * @private - */ - var setUpPlayerIntervals_ = function() { - this.updateTimeIntervalHandle = - setInterval(updateCurrentTime_, this.seekCheckInterval); - this.seekCheckIntervalHandle = - setInterval(checkForSeeking_, this.seekCheckInterval); - this.resizeCheckIntervalHandle = - setInterval(checkForResize_, this.resizeCheckInterval); - }.bind(this); - - /** - * Updates the start time of the video - * @private - */ - var updateStartTime_ = function(){ - var cur = this.player.currentTime(); - if (!cur || this.player.ads.state!='content-playback') - return; - // first time that isn't zero is our start time, but only if it's - // more than the 1sec - if (cur<1) - cur = 0; - this.contentPlayheadTracker.startTime = cur; - this.player.off('timeupdate', updateStartTime_); - }.bind(this); - - /** - * Updates the current time of the video - * @private - */ - var updateCurrentTime_ = function() { - if (this.player.ads.state=='content-playback' && - !this.contentPlayheadTracker.seeking && - this.contentPlayheadTracker.startTime>=0) { - this.contentPlayheadTracker.currentTime = this.player.currentTime() - - this.contentPlayheadTracker.startTime; - } - }.bind(this); - - /** - * Detects when the user is seeking through a video. - * This is used to prevent mid-rolls from playing while a user is seeking. - * - * There *is* a seeking property of the HTML5 video element, but it's not - * properly implemented on all platforms (e.g. mobile safari), so we have to - * check ourselves to be sure. - * - * @private - */ - var checkForSeeking_ = function() { - if (this.player.ads.state!='content-playback') - return; - var tempCurrentTime = this.player.currentTime(); - var diff = (tempCurrentTime - this.contentPlayheadTracker.previousTime) * 1000; - if (Math.abs(diff) > this.seekCheckInterval + this.seekThreshold) { - this.contentPlayheadTracker.seeking = true; - } else { - this.contentPlayheadTracker.seeking = false; - } - this.contentPlayheadTracker.previousTime = this.player.currentTime(); - }.bind(this); - - /** - * Detects when the player is resized (for fluid support) and resizes the - * ads manager to match. - * - * @private - */ - var checkForResize_ = function() { - var currentWidth = this.getPlayerWidth(); - var currentHeight = this.getPlayerHeight(); - - if (this.adsManager && (currentWidth != this.adsManagerDimensions.width || - currentHeight != this.adsManagerDimensions.height)) { - this.adsManagerDimensions.width = currentWidth; - this.adsManagerDimensions.height = currentHeight; - this.adsManager.resize(currentWidth, currentHeight, google.ima.ViewMode.NORMAL); - } - }.bind(this); - - /** - * Changes the flag to show or hide the ad countdown timer. - * - * @param {boolean} showCountdownIn Show or hide the countdown timer. - */ - this.setShowCountdown = function(showCountdownIn) { - this.showCountdown = showCountdownIn; - this.countdownDiv.style.display = this.showCountdown ? '' : 'none'; - }.bind(this); - - /** - * Current plugin version. - */ - this.VERSION = '0.2.0'; - - /** - * Stores user-provided settings. - */ - this.settings; - - /** - * Used to prefix videojs ima - */ - this.controlPrefix; - - /** - * Video element playing content. - */ - this.contentPlayer; - - /** - * Boolean flag to show or hide the ad countdown timer. - */ - this.showCountdown; - - /** - * Boolena flag to enable manual ad break playback. - */ - this.autoPlayAdBreaks; - - /** - * Video.js control bar. - */ - this.vjsControls; - - /** - * Div used as an ad container. - */ - this.adContainerDiv; - - /** - * Div used to display ad controls. - */ - this.controlsDiv; - - /** - * Div used to display ad countdown timer. - */ - this.countdownDiv; - - /** - * Div used to display add seek bar. - */ - this.seekBarDiv; - - /** - * Div used to display ad progress (in seek bar). - */ - this.progressDiv; - - /** - * Div used to display ad play/pause button. - */ - this.playPauseDiv; - - /** - * Div used to display ad mute button. - */ - this.muteDiv; - - /** - * Div used by the volume slider. - */ - this.sliderDiv; - - /** - * Volume slider level visuals - */ - this.sliderLevelDiv; - - /** - * Div used to display ad fullscreen button. - */ - this.fullscreenDiv; - - /** - * IMA SDK AdDisplayContainer. - */ - this.adDisplayContainer; - - /** - * True if the AdDisplayContainer has been initialized. False otherwise. - */ - this.adDisplayContainerInitialized = false; - - /** - * IMA SDK AdsLoader - */ - this.adsLoader; - - /** - * IMA SDK AdsManager - */ - this.adsManager; - - /** - * IMA SDK AdsRenderingSettings. - */ - this.adsRenderingSettings = null; - - /** - * Ad tag URL. Should return VAST, VMAP, or ad rules. - */ - this.adTagUrl; - - /** - * VAST, VMAP, or ad rules response. Used in lieu of fetching a response - * from an ad tag URL. - */ - this.adsResponse; - - /** - * Current IMA SDK Ad. - */ - this.currentAd; - - /** - * Timer used to track content progress. - */ - this.contentTrackingTimer; - - /** - * Timer used to track ad progress. - */ - this.adTrackingTimer; - - /** - * True if ads are currently displayed, false otherwise. - * True regardless of ad pause state if an ad is currently being displayed. - */ - this.adsActive = false; - - /** - * True if ad is currently playing, false if ad is paused or ads are not - * currently displayed. - */ - this.adPlaying = false; - - /** - * True if the ad is muted, false otherwise. - */ - this.adMuted = false; - - /** - * True if our content video has completed, false otherwise. - */ - this.contentComplete = false; - - /** - * True if ALL_ADS_COMPLETED has fired, false until then. - */ - this.allAdsCompleted = false; - - /** - * Handle to interval that repeatedly updates current time. - */ - this.updateTimeIntervalHandle; - - /** - * Handle to interval that repeatedly checks for seeking. - */ - this.seekCheckIntervalHandle; - - /** - * Interval (ms) on which to check if the user is seeking through the - * content. - */ - this.seekCheckInterval = 1000; - - /** - * Handle to interval that repeatedly checks for player resize. - */ - this.resizeCheckIntervalHandle; - - /** - * Interval (ms) to check for player resize for fluid support. - */ - this.resizeCheckInterval = 250; - - /** - * Threshold by which to judge user seeking. We check every 1000 ms to see - * if the user is seeking. In order for us to decide that they are *not* - * seeking, the content video playhead must only change by 900-1100 ms - * between checks. Any greater change and we assume the user is seeking - * through the video. - */ - this.seekThreshold = 100; - - /** - * Stores data for the content playhead tracker. - */ - this.contentPlayheadTracker = { - currentTime: 0, - previousTime: 0, - seeking: false, - duration: 0, - startTime: -1 - }; - - /** - * Stores data for the ad playhead tracker. - */ - this.adPlayheadTracker = { - currentTime: 0, - duration: 0, - isPod: false, - adPosition: 0, - totalAds: 0 - }; - - /** - * Stores the dimensions for the ads manager. - */ - this.adsManagerDimensions = { - width: 0, - height: 0 - }; - - /** - * Content ended listeners passed by the publisher to the plugin. Publishers - * should allow the plugin to handle content ended to ensure proper support - * of custom ad playback. - */ - this.contentEndedListeners = []; - - /** - * Content and ads ended listeners passed by the publisher to the plugin. - * These will be called when the plugin detects that content *and all - * ads* have completed. This differs from the contentEndedListeners in that - * contentEndedListeners will fire between content ending and a post-roll - * playing, whereas the contentAndAdsEndedListeners will fire after the - * post-roll completes. - */ - this.contentAndAdsEndedListeners = []; - - /** - * Listener to be called to trigger manual ad break playback. - */ - this.adBreakReadyListener = function() { - console.log('Please set adBreakReadyListener'); - }; - - /** - * Stores the content source so we can re-populate it manually after a - * post-roll on iOS. - */ - this.contentSource = ''; - - /** - * Local content ended listener for contentComplete. - */ - this.localContentEndedListener = function() { - if (this.adsLoader && !this.contentComplete) { - this.adsLoader.contentComplete(); - this.contentComplete = true; - } - for (var index in this.contentEndedListeners) { - this.contentEndedListeners[index](); - } - if (this.allAdsCompleted) { - for (var index in this.contentAndAdsEndedListeners) { - this.contentAndAdsEndedListeners[index](); - } - } - clearInterval(this.updateTimeIntervalHandle); - clearInterval(this.seekCheckIntervalHandle); - clearInterval(this.resizeCheckIntervalHandle); - if(this.player.el()) { - this.player.one('play', setUpPlayerIntervals_); - } - }.bind(this); - - this.playerDisposedListener = function(){ - this.contentEndedListeners, this.contentAndAdsEndedListeners = [], []; - this.contentComplete = true; - this.player.off('contentended', this.localContentEndedListener); - this.player.off('timeupdate', updateStartTime_); - - // Bug fix: https://github.com/googleads/videojs-ima/issues/306 - if (this.player.ads.adTimeoutTimeout) { - clearTimeout(this.player.ads.adTimeoutTimeout); - } - - var intervalsToClear = [this.updateTimeIntervalHandle, this.seekCheckIntervalHandle, - this.adTrackingTimer, this.resizeCheckIntervalHandle]; - for (var index in intervalsToClear) { - var interval = intervalsToClear[index]; - if (interval) { - clearInterval(interval); - } - } - if (this.adsManager) { - this.adsManager.destroy(); - this.adsManager = null; - } - }.bind(this); - - this.initVjsControls = function() { - var _this = this; - var override = function(cls, obj, method, fn, always) { - var orig = cls.prototype[method]; - return obj[method] = function() { - return _this.adsActive || always ? fn && fn.apply(this, arguments) : - orig && orig.apply(this, arguments); - }; - }; - var overrideHandler = function(cls, obj, target, event, method, fn, always) { - var orig = cls.prototype[method]; - var handler = override(cls, obj, method, fn); - if (target) { - obj.off(target, event, orig); - obj.on(target, event, handler); - } else { - obj.off(event, orig); - obj.on(event, handler); - } - }; - var PlayToggle = videojs.getComponent('PlayToggle'); - var playToggle = this.vjsControls.playToggle; - overrideHandler(PlayToggle, playToggle, null, ['tap', 'click'], - 'handleClick', function() { - onAdPlayPauseClick_(); - if (_this.adPlaying) { - this.handlePlay(); - } else { - this.handlePause(); - } - }); - override(PlayToggle, playToggle, 'update', function() { - var paused = _this.adsActive ? !_this.adPlaying : player.paused(); - this.toggleClass('vjs-play-control-ad', _this.adsActive); - this.toggleClass('vjs-paused', paused); - this.toggleClass('vjs-playing', !paused); - var text = paused ? 'Play' : 'Pause'; - if (text != this.controlText()) - this.controlText(text); - }, true); - overrideHandler(PlayToggle, playToggle, player, 'play', - 'handlePlay', function() { this.update(); }, true); - overrideHandler(PlayToggle, playToggle, player, 'pause', - 'handlePause', function() { this.update(); }, true); - - var SeekBar = videojs.getComponent('SeekBar'); - var DvrSeekBar = videojs.getComponent('DvrSeekBar'); - var seekBar = this.vjsControls.progressControl.seekBar; - var getPercent = function() { - var duration = _this.currentAd && _this.currentAd.getDuration(); - if (!duration || duration<0) { - return 0; - } - var remainingTime = _this.adsManager.getRemainingTime(); - var currentTime = Math.max(duration - remainingTime, 0); - return currentTime / duration; - }; - override(SeekBar, seekBar, 'getPercent', getPercent); - if (DvrSeekBar) { - override(DvrSeekBar, seekBar, 'getPercent', getPercent); - } - overrideHandler(SeekBar, seekBar, null, ['mousedown', 'touchstart'], - 'handleMouseDown', null); - overrideHandler(SeekBar, seekBar, null, 'focus', 'handleFocus', null); - - var DurationDisplay = videojs.getComponent('DurationDisplay'); - var durationDisplay = this.vjsControls.durationDisplay; - overrideHandler(DurationDisplay, durationDisplay, player, - ['timeupdate', 'loadedmetadata'], 'updateContent', function() { - var duration = _this.currentAd && _this.currentAd.getDuration(); - if (duration && duration != this.duration_) { - this.duration_ = duration; - this.contentEl_.innerHTML = ''+ - this.localize('Duration Time')+' '+formatTime(duration); - } - }); - - var CurrentTimeDisplay = videojs.getComponent('CurrentTimeDisplay'); - var currentTimeDisplay = this.vjsControls.currentTimeDisplay; - overrideHandler(CurrentTimeDisplay, currentTimeDisplay, player, - ['timeupdate', 'loadedmetadata'], 'updateContent', function() { - var duration = _this.currentAd && _this.currentAd.getDuration(); - if (!duration) { - return; - } - var time = duration - _this.adsManager.getRemainingTime(); - var formattedTime = formatTime(time); - if (formattedTime !== this.formattedTime_) { - this.formattedTime_ = formattedTime; - this.contentEl_.innerHTML = ''+ - this.localize('Current Time')+' '+formattedTime; - } - }); - }.bind(this); - - this.updateVjsControls = function() { - if (!this.settings.vjsControls) { - return; - } - this.player.toggleClass('vjs-ad-paused', - this.adsActive && !this.adPlaying); - var controls = this.vjsControls; - controls.playToggle.update(); - controls.progressControl.seekBar.update(); - controls.durationDisplay.updateContent(); - controls.currentTimeDisplay.updateContent(); - var duration = this.currentAd && this.currentAd.getDuration(); - var display = !this.adsActive || duration && duration>=0 ? '' : 'none'; - controls.durationDisplay.el().style.display = display; - controls.currentTimeDisplay.el().style.display = display; - controls.timeDivider.el().style.display = display; - }.bind(this); - - var getPosition = function(el) { - var box = el.getBoundingClientRect(); - var docEl = document.documentElement; - var body = document.body; - var clientLeft = docEl.clientLeft || body.clientLeft || 0; - var scrollLeft = window.pageXOffset || body.scrollLeft; - var left = box.left + scrollLeft - clientLeft; - var clientTop = docEl.clientTop || body.clientTop || 0; - var scrollTop = window.pageYOffset || body.scrollTop; - var top = box.top + scrollTop - clientTop; - return { - left: left, - top: top, - width: box.width, - height: box.height, - }; - }; - - // proxy click events to the video element when non-linear ad is active - this.proxyClickEvents = function() { - var events = (videojs && videojs.browser && videojs.browser.IS_ANDROID || videojs.browser.IS_IOS) ? - ['touchstart', 'touchend'] : - ['click', 'dblclick', 'mousedown', 'mouseup']; - var player = this.player, el = player.el(), _this = this; - events.forEach(function(eventName) { - el.addEventListener(eventName, function(e) { - var ad = _this.currentAd, t = e.target; - if (!ad || ad.isLinear() || t.nodeName!='IFRAME' || e.isTrusted) { - return; - } - // ignore clicks on ad ui elements - var adWidth = ad.getWidth() || ad.getVastMediaWidth(); - var adHeight = ad.getHeight() || ad.getVastMediaHeight(); - var pos = getPosition(t); - var touch = e.touches && e.touches[0]; - var x = touch ? touch.pageX : e.clientX; - var y = touch ? touch.pageY : e.clientY; - var adRight = pos.left+pos.width-(pos.width-adWidth)/2; - var adTop = pos.top+pos.height-adHeight-4; - // click on close button - if (x(adRight-40) && y>adTop && y<(adTop+30)) { - return; - } - // click on recall button - if (x>(pos.left+pos.width/2-15) && x<(pos.left+pos.width/2+15) && - y>(pos.top+pos.height-15)) { - return; - } - var newEvent; - var opt = {}; - for (var key in e) { - opt[key] = e[key]; - } - opt.bubbles = false; - try { - newEvent = new e.constructor(e.type, opt); - } catch (err) { - // special case for IE11 - newEvent = document.createEvent('MouseEvent'); - newEvent.initMouseEvent(e.type, opt.bubbles, opt.cancelable, - opt.view, opt.detail, opt.screenX, opt.screenY, opt.clientX, - opt.clientY, opt.ctrlKey, opt.altKey, opt.shiftKey, opt.metaKey, - opt.button, null); - } - newEvent.stopPropagation(); - player.tech_.trigger(newEvent); - }); - }); - }.bind(this); - - this.settings = extend({}, ima_defaults, options || {}); - this.settings.adLabel = this.player.localize(this.settings.adLabel); - - // Currently this isn't used but I can see it being needed in the future, so - // to avoid implementation problems with later updates I'm requiring it. - if (!this.settings['id']) { - window.console.log('Error: must provide id of video.js div'); - return; - } - - this.controlPrefix = (this.settings.id + '_') || ''; - - this.contentPlayer = this.player.$('.vjs-tech'); - // Default showing countdown timer to true. - this.showCountdown = true; - if (this.settings['showCountdown'] === false) { - this.showCountdown = false; - } - - this.autoPlayAdBreaks = true; - if (this.settings['autoPlayAdBreaks'] === false) { - this.autoPlayAdBreaks = false; - } - - var contrib_ads_defaults = { - debug: this.settings.debug, - timeout: this.settings.timeout, - prerollTimeout: this.settings.prerollTimeout, - postrollTimeout: this.settings.postrollTimeout - }; - - var ads_plugin_settings = - extend({}, contrib_ads_defaults, options['contribAdsSettings'] || {}); - - player.ads(ads_plugin_settings); - - player.one('play', setUpPlayerIntervals_); - player.on('contentended', this.localContentEndedListener); - player.on('dispose', this.playerDisposedListener); - player.on('timeupdate', updateStartTime_); - this.adsRenderingSettings = new google.ima.AdsRenderingSettings(); - this.adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true; - if (this.settings['adsRenderingSettings']) { - for (var setting in this.settings['adsRenderingSettings']) { - this.adsRenderingSettings[setting] = - this.settings['adsRenderingSettings'][setting]; - } - } - - if (this.settings['locale']) { - google.ima.settings.setLocale(this.settings['locale']); - } - - createAdContainer_(); - this.adsLoader = new google.ima.AdsLoader(this.adDisplayContainer); - - this.adsLoader.getSettings().setVpaidMode( - google.ima.ImaSdkSettings.VpaidMode.ENABLED); - if (this.settings.vpaidAllowed == false) { - this.adsLoader.getSettings().setVpaidMode( - google.ima.ImaSdkSettings.VpaidMode.DISABLED); - } - if (this.settings.vpaidMode) { - this.adsLoader.getSettings().setVpaidMode(this.settings.vpaidMode); - } - - if (this.settings.locale) { - this.adsLoader.getSettings().setLocale(this.settings.locale); - } - - if (this.settings.numRedirects) { - this.adsLoader.getSettings().setNumRedirects(this.settings.numRedirects); - } - - this.adsLoader.getSettings().setPlayerType('videojs-ima'); - this.adsLoader.getSettings().setPlayerVersion(this.VERSION); - this.adsLoader.getSettings().setAutoPlayAdBreaks(this.autoPlayAdBreaks); - - this.adsLoader.addEventListener( - google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, - onAdsManagerLoaded_, - false); - this.adsLoader.addEventListener( - google.ima.AdErrorEvent.Type.AD_ERROR, - onAdsLoaderError_, - false); - - if (!readyCallback) { - readyCallback = this.startFromReadyCallback; - } - player.on('readyforpreroll', readyCallback); - player.ready(function() { - player.on('fullscreenchange', onFullscreenChange_); - player.on('volumechange', onVolumeChange_); - }); - this.proxyClickEvents(); - }; - - videojs.registerPlugin('ima', init); -}); +import 'videojs-contrib-ads'; +import 'videojs-ima'; +import 'videojs-ima/dist/videojs.ima.scss'; diff --git a/src/plugins/ima/index.js b/src/plugins/ima/index.js index 43873798..70867f71 100644 --- a/src/plugins/ima/index.js +++ b/src/plugins/ima/index.js @@ -1,11 +1,9 @@ /* global google */ import { isFunction } from 'utils/type-inference'; import { PLAYER_EVENT } from 'utils/consts'; -import 'videojs-contrib-ads'; -import './ima'; -import './videojs-ima.scss'; -export default function imaPlugin(player, playerOptions) { +export default async function imaPlugin(player, playerOptions) { + await import(/* webpackChunkName: "ima" */ './ima'); const loaded = { contribAdsLoaded: isFunction(player.ads), @@ -38,11 +36,11 @@ export default function imaPlugin(player, playerOptions) { if (Object.keys(playerOptions.ads).length > 0 && typeof player.ima === 'object') { if (playerOptions.ads.adsInPlaylist === 'first-video') { player.one(PLAYER_EVENT.SOURCE_CHANGED, () => { - player.ima.playAd(); + player.ima.playAdBreak(); }); } else { player.on(PLAYER_EVENT.SOURCE_CHANGED, () => { - player.ima.playAd(); + player.ima.playAdBreak(); }); } } diff --git a/src/plugins/ima/videojs-ima.scss b/src/plugins/ima/videojs-ima.scss deleted file mode 100644 index 8d796355..00000000 --- a/src/plugins/ima/videojs-ima.scss +++ /dev/null @@ -1,252 +0,0 @@ -.cld-video-player { - .ima-fullscreen-div, .ima-mute-div, .ima-play-pause-div, .ima-slider-div { - line-height: 1.7; - } - - &.vjs-ad-loading { - > video, - > .vjs-poster { - opacity: 0; - } - } -} - -.ima-ad-container { - top: 0; - left: 0; - position: absolute; - display: block; - width: 100%; - height: 100%; - video { - left: 0; - } -} - -/* Move overlay if user fast-clicks play button. */ -.video-js.vjs-playing .bumpable-ima-ad-container { - margin-top: -50px; -} - -/* Move overlay when controls are active. */ -.video-js.vjs-user-inactive.vjs-playing .bumpable-ima-ad-container { - margin-top: 0px; -} - -.video-js.vjs-paused .bumpable-ima-ad-container, -.video-js.vjs-playing:hover .bumpable-ima-ad-container, -.video-js.vjs-user-active.vjs-playing .bumpable-ima-ad-container { - margin-top: -50px; -} - -.vjs-ima-non-linear .vjs-big-play-button, -.vjs-ima-non-linear .vjs-menu.vjs-settings-menu, -.vjs-ima-non-linear .vjs-info-overlay, -.vjs-ima-non-linear .vjs-modal-overlay, -.vjs-ima-non-linear .vjs-control-bar { - z-index: 1112; -} - -.ima-controls-div { - bottom:0px; - height: 37px; - position: absolute; - overflow: hidden; - display: none; - opacity: 1; - background-color: rgba(7, 20, 30, .7); - background: -moz-linear-gradient( - bottom, - rgba(7, 20, 30, .7) 0%, - rgba(7, 20, 30, 0) 100%); /* FF3.6+ */ - background: -webkit-gradient( - linear, - left bottom, - left top, - color-stop(0%,rgba(7, 20, 30, .7)), - color-stop(100%,rgba(7, 20, 30, 0))); /* Chrome,Safari4+ */ - background: -webkit-linear-gradient( - bottom, - rgba(7, 20, 30, .7) 0%, - rgba(7, 20, 30, 0) 100%); /* Chrome10+,Safari5.1+ */ - background: -o-linear-gradient(bottom, - rgba(7, 20, 30, .7) 0%, - rgba(7, 20, 30, 0) 100%); /* Opera 11.10+ */ - background: -ms-linear-gradient(bottom, - rgba(7, 20, 30, .7) 0%, - rgba(7, 20, 30, 0) 100%); /* IE10+ */ - background: linear-gradient(to top, - rgba(7, 20, 30, .7) 0%, - rgba(7, 20, 30, 0) 100%); /* W3C */ - filter: progid:DXImageTransform.Microsoft.gradient( - startColorstr='#0007141E', - endColorstr='#07141E',GradientType=0 ); /* IE6-9 */ -} - -.ima-countdown-div { - height: 10px; - color: #FFFFFF; - text-shadow: 0 0 0.2em #000; - cursor: default; -} - -.ima-seek-bar-div { - top: 12px; - height: 3px; - position: absolute; - background: rgba(255, 255, 255, .4); -} - -.ima-progress-div { - width: 0px; - height: 3px; - background-color: #ECC546; -} - -.ima-play-pause-div, .ima-mute-div, .ima-slider-div, .ima-fullscreen-div { - width: 35px; - height: 20px; - top: 11px; - left: 0px; - position: absolute; - color: #CCCCCC; - font-size: 1.5em; - line-height: 2; - text-align: center; - font-family: VideoJS; - cursor: pointer; -} - -.ima-mute-div { - left: auto; - right: 85px; -} - -.ima-slider-div { - left: auto; - right: 35px; - width: 50px; - height: 10px; - top: 20px; - background-color: #555555; -} - -.ima-slider-level-div { - width: 100%; - height: 10px; - background-color: #ECC546; -} - -.ima-fullscreen-div { - left: auto; - right: 0px; -} - -.ima-playing:before { - content: "\00f103"; -} - -.ima-paused:before { - content: "\00f101"; -} - -.ima-playing:hover:before, .ima-paused:hover:before { - text-shadow: 0 0 1em #fff; -} - -.ima-non-muted:before { - content: "\00f107"; -} - -.ima-muted:before { - content: "\00f104"; -} - -.ima-non-muted:hover:before, .ima-muted:hover:before { - text-shadow: 0 0 1em #fff; -} - -.ima-non-fullscreen:before { - content: "\00f108"; -} - -.ima-fullscreen:before { - content: "\00f109"; -} - -.ima-non-fullscreen:hover:before, .ima-fullscreen:hover:before { - text-shadow: 0 0 1em #fff; -} - -.video-js.vjs-ad-playing .vjs-control-bar { - z-index: 1112; -} - -.video-js.vjs-ad-playing .vjs-control-bar .vjs-progress-control { - pointer-events: none; -} - -.video-js.vjs-ad-playing .vjs-control-bar>:not(.vjs-play-control):not(.vjs-volume-menu-button):not(.vjs-time-control):not(.vjs-progress-control):not(.vjs-spacer):not(.vjs-fullscreen-control):not(.vjs-gradient):not(.ima-countdown-div), -.video-js.vjs-ad-playing .vjs-control-bar .vjs-mouse-display-tooltip, -.video-js.vjs-ad-playing .vjs-control-bar .vjs-progress-control:before, -.video-js.vjs-ad-playing .vjs-control-bar .vjs-play-progress:before, -.video-js.vjs-ad-playing .vjs-control-bar .vjs-progress-control .vjs-load-progress, -.video-js.vjs-ad-playing .vjs-control-bar .vjs-progress-control .vjs-mouse-display { - display: none; -} - -.video-js.vjs-ad-playing .vjs-control-bar { - transition: height 0.1s ease; -} - -.video-js.vjs-ad-playing.vjs-user-inactive:not(.vjs-ad-paused) .vjs-control-bar { - opacity: 1; - height: 0; -} - -.video-js.vjs-ad-playing.vjs-user-inactive:not(.vjs-ad-paused) .vjs-control-bar .vjs-progress-holder { - margin: 0; -} - -.video-js.vjs-ad-playing .vjs-control-bar>:not(.vjs-progress-control):not(.ima-countdown-div) { - transition: opacity 0.1s ease, visibility 0.1s ease; -} - -.video-js.vjs-ad-playing .vjs-control-bar>:not(.vjs-progress-control):not(.ima-countdown-div) div{ - transition: line-height 0.1s ease; -} - -.video-js.vjs-ad-playing.vjs-user-inactive:not(.vjs-ad-paused) .vjs-control-bar>:not(.vjs-progress-control):not(.ima-countdown-div) { - opacity: 0; - visibility: hidden; - overflow: hidden; -} - -.video-js.vjs-ad-playing.vjs-user-inactive:not(.vjs-ad-paused) .vjs-control-bar>:not(.vjs-progress-control):not(.ima-countdown-div) div{ - line-height: 0.01em; -} - -.video-js.vjs-ad-playing .vjs-control-bar .vjs-progress-control .vjs-thumbnail-holder, -.video-js.vjs-ad-playing .vjs-control-bar .vjs-progress-control .vjs-thumbnail { - display: none !important; -} - -.video-js.vjs-ad-playing .vjs-control-bar .vjs-progress-control .vjs-progress-holder { - cursor: auto; - box-shadow: none; - font-size: 100%; - margin: 0 1em; -} - -.video-js .vjs-control-bar .ima-countdown-div { - display: none; - z-index: 1; -} - -.video-js.vjs-ad-playing .vjs-control-bar .ima-countdown-div { - display: block; - position: absolute; - margin-top: -2em; - left: 2em; - font-size: 1.2em; -} diff --git a/src/plugins/index.js b/src/plugins/index.js index a628993a..2f4deba3 100644 --- a/src/plugins/index.js +++ b/src/plugins/index.js @@ -1,16 +1,12 @@ import 'videojs-per-source-behaviors'; // #if (!process.env.WEBPACK_BUILD_LIGHT) -import dashPlugin from './dash'; -import imaPlugin from './ima'; -import interactive from './interactive-plugin'; import './videojs-http-source-selector/plugin'; // #endif import aiHighlightsGraph from './ai-highlights-graph'; import analytics from './analytics'; import autoplayOnScroll from './autoplay-on-scroll'; -import chapters from './chapters'; import cloudinary from './cloudinary'; import cloudinaryAnalytics from './cloudinary-analytics'; import colors from './colors'; @@ -20,26 +16,34 @@ import pacedTranscript from './paced-transcript'; import vttThumbnails from './vtt-thumbnails'; // Lazy loaded plugins +import chapters from './chapters'; +import dashPlugin from './dash'; +import imaPlugin from './ima'; +import playlist from './playlist'; +import shoppable from './shoppable-plugin'; import styledTextTracks from './styled-text-tracks'; +import interactionAreas from './interaction-areas'; const plugins = { - // #if (!process.env.WEBPACK_BUILD_LIGHT) - dashPlugin, - interactive, - imaPlugin, - // #endif aiHighlightsGraph, analytics, autoplayOnScroll, - chapters, cloudinary, cloudinaryAnalytics, colors, contextMenu, floatingPlayer, pacedTranscript, + vttThumbnails, + + // Lazy loaded plugins + chapters, + dashPlugin, + imaPlugin, + playlist, + shoppable, styledTextTracks, - vttThumbnails + interactionAreas }; export default plugins; diff --git a/src/plugins/interaction-areas/index.js b/src/plugins/interaction-areas/index.js new file mode 100644 index 00000000..941dfd34 --- /dev/null +++ b/src/plugins/interaction-areas/index.js @@ -0,0 +1,8 @@ +export default async function lazyInteractionAreasPlugin(player, playerOptions, videojsOptions) { + try { + const { interactionAreasService } = await import(/* webpackChunkName: "interaction-areas" */ './interaction-areas.service'); + interactionAreasService(player, playerOptions, videojsOptions); + } catch (error) { + console.error('Failed to load plugin:', error); + } +} diff --git a/src/components/interaction-area/interaction-area.const.js b/src/plugins/interaction-areas/interaction-areas.const.js similarity index 100% rename from src/components/interaction-area/interaction-area.const.js rename to src/plugins/interaction-areas/interaction-areas.const.js diff --git a/src/assets/styles/components/interaction-areas.scss b/src/plugins/interaction-areas/interaction-areas.scss similarity index 100% rename from src/assets/styles/components/interaction-areas.scss rename to src/plugins/interaction-areas/interaction-areas.scss diff --git a/src/components/interaction-area/interaction-area.service.js b/src/plugins/interaction-areas/interaction-areas.service.js similarity index 97% rename from src/components/interaction-area/interaction-area.service.js rename to src/plugins/interaction-areas/interaction-areas.service.js index 98826136..e55be563 100644 --- a/src/components/interaction-area/interaction-area.service.js +++ b/src/plugins/interaction-areas/interaction-areas.service.js @@ -3,7 +3,7 @@ import { CLOSE_INTERACTION_AREA_LAYOUT_DELAY, DEFAULT_INTERACTION_ARE_TRANSITION, INTERACTION_AREAS_CONTAINER_CLASS_NAME, TEMPLATE_INTERACTION_AREAS_VTT -} from './interaction-area.const'; +} from './interaction-areas.const'; import { createInteractionAreaLayoutMessage, getInteractionAreaItem, @@ -13,7 +13,7 @@ import { setInteractionAreasContainerSize, shouldShowAreaLayoutMessage, updateInteractionAreasItem -} from './interaction-area.utils'; +} from './interaction-areas.utils'; import { addEventListener, createElement } from '../../utils/dom'; import { throttle } from '../../utils/throttle'; import { get } from '../../utils/object'; @@ -21,8 +21,9 @@ import { noop } from '../../utils/type-inference'; import { addMetadataTrack } from '../../video-player.utils'; import { PLAYER_EVENT } from '../../utils/consts'; +import './interaction-areas.scss'; -export const interactionAreaService = (player, playerOptions, videojsOptions) => { +export const interactionAreasService = (player, playerOptions, videojsOptions) => { let isZoomed = false; let currentSource = null; @@ -240,8 +241,5 @@ export const interactionAreaService = (player, playerOptions, videojsOptions) => }); } - return { - init - }; - + init(); }; diff --git a/src/components/interaction-area/interaction-area.utils.js b/src/plugins/interaction-areas/interaction-areas.utils.js similarity index 97% rename from src/components/interaction-area/interaction-area.utils.js rename to src/plugins/interaction-areas/interaction-areas.utils.js index daa4a801..af3e02a9 100644 --- a/src/components/interaction-area/interaction-area.utils.js +++ b/src/plugins/interaction-areas/interaction-areas.utils.js @@ -7,12 +7,12 @@ import { INTERACTION_AREAS_CONTAINER_CLASS_NAME, INTERACTION_AREAS_PREFIX, INTERACTION_AREAS_THEME -} from './interaction-area.const'; +} from './interaction-areas.const'; import { noop } from '../../utils/type-inference'; -import { getDefaultPlayerColor } from '../../plugins/colors'; +import { getDefaultPlayerColor } from '../colors'; import { forEach, some } from '../../utils/array'; -import { themedButton } from '../themeButton/themedButton'; -import { BUTTON_THEME } from '../themeButton/themedButton.const'; +import { themedButton } from '../../components/themeButton/themedButton'; +import { BUTTON_THEME } from '../../components/themeButton/themedButton.const'; const getInteractionAreaItemId = (item, index) => item.id || item.type || `id_${index}`; diff --git a/src/plugins/interactive-plugin/index.js b/src/plugins/interactive-plugin/index.js deleted file mode 100644 index 626039ac..00000000 --- a/src/plugins/interactive-plugin/index.js +++ /dev/null @@ -1,10 +0,0 @@ -import ShoppableWidget from '../../components/shoppable-bar/shoppable-widget'; - -export default function interactivePlugin(player, options) { - - // Shoppable Video - if (options.shoppable) { - new ShoppableWidget(player, options.shoppable).init(); - } - -} diff --git a/src/plugins/playlist/index.js b/src/plugins/playlist/index.js new file mode 100644 index 00000000..091b75a6 --- /dev/null +++ b/src/plugins/playlist/index.js @@ -0,0 +1,11 @@ +export default async function lazyPlugin(options) { + const player = this; + try { + const { default: playlistPlugin } = await import(/* webpackChunkName: "playlist" */ './playlist'); + const playlist = playlistPlugin(player, options); + player.cloudinary.playlist = playlist; + return playlist; + } catch (error) { + console.error('Failed to load plugin:', error); + } +} diff --git a/src/plugins/playlist/playlist.js b/src/plugins/playlist/playlist.js new file mode 100644 index 00000000..c99167c2 --- /dev/null +++ b/src/plugins/playlist/playlist.js @@ -0,0 +1,125 @@ +import { sliceProperties } from 'utils/slicing'; +import { normalizeJsonResponse } from 'utils/api'; +import { PLAYER_EVENT } from 'utils/consts'; +import { isPlainObject } from 'utils/type-inference'; +import { getCloudinaryUrl } from 'plugins/cloudinary/common'; + +import Playlist from './ui/playlist'; +import PlaylistWidget from './ui/playlist-widget'; +import './ui/panel/playlist-panel'; + +const LIST_BY_TAG_PARAMS = { format: 'json', resource_type: 'video', type: 'list' }; + +const playlist = (player, options = {}) => { + const chainTarget = sliceProperties(options, 'chainTarget').chainTarget; + let playlistInstance = null; + let playlistDisposer = null; + let playlistWidget = null; + + const initPlaylistWidget = () => { + player.on(PLAYER_EVENT.PLAYLIST_CREATED, () => { + if (playlistWidget) { + playlistWidget.dispose(); + } + + if (isPlainObject(options.playlistWidget)) { + if (player.fluid_) { + options.playlistWidget.fluid = true; + } + + if (player.cloudinary.fontFace) { + options.playlistWidget.fontFace = player.cloudinary.fontFace; + } + + playlistWidget = new PlaylistWidget(player, options.playlistWidget); + } + }); + }; + + const disposePlaylist = () => { + player.removeClass('vjs-playlist'); + playlistInstance = undefined; + player.playlist().dispose(); + player.off('cldsourcechanged', playlistDisposer); + }; + + const addPlaylistDisposer = () => { + const disposer = () => { + if ( + playlistInstance && + !playlistInstance.currentSource().contains(player.currentSource()) + ) { + player.disposePlaylist(); + } + }; + + player.on('cldsourcechanged', disposer); + + return disposer; + }; + + const createPlaylist = (sources, options) => { + if (sources instanceof Playlist) { + playlistInstance = sources; + playlistInstance.resetState(); + playlistInstance.currentIndex(playlistInstance.currentIndex()); + } else { + playlistInstance = new Playlist(player.cloudinary, sources, options); + playlistInstance.currentIndex(0); + } + + initPlaylistWidget(); + + playlistDisposer = addPlaylistDisposer(); + player.addClass('vjs-playlist'); + }; + + player.cloudinary.sourcesByTag = async (tag, options = {}) => { + const url = getCloudinaryUrl( + tag, + Object.assign({}, player.cloudinary.cloudinaryConfig(), LIST_BY_TAG_PARAMS) + ); + + const result = await fetch(url); + const json = await result.json(); + + const resources = normalizeJsonResponse(json.resources); + + if (options.sorter) { + resources.sort(options.sorter); + } + + const sources = resources.map(resource => { + let sourceParams = options.sourceParams || {}; + + if (typeof sourceParams === 'function') { + sourceParams = sourceParams(resource); + } + + const info = (resource.context && resource.context.custom) || {}; + + const source = Object.assign({ info }, sourceParams, { publicId: resource.publicId }); + + return player.cloudinary.buildSource(source); + }); + + return sources; + }; + + return (sources, options = {}) => { + if (sources === undefined) { + return playlistInstance; + } + + if (playlistInstance) { + disposePlaylist(); + } + + createPlaylist(sources, options); + player.trigger('playlistcreated'); + + return chainTarget; + }; +}; + +export default playlist; diff --git a/src/components/playlist/components/playlist-button.js b/src/plugins/playlist/ui/components/playlist-button.js similarity index 100% rename from src/components/playlist/components/playlist-button.js rename to src/plugins/playlist/ui/components/playlist-button.js diff --git a/src/components/playlist/components/playlist.js b/src/plugins/playlist/ui/components/playlist-buttons.js similarity index 83% rename from src/components/playlist/components/playlist.js rename to src/plugins/playlist/ui/components/playlist-buttons.js index 58433655..b2e8f348 100644 --- a/src/components/playlist/components/playlist.js +++ b/src/plugins/playlist/ui/components/playlist-buttons.js @@ -1,5 +1,5 @@ import PlaylistNextButton from './playlist-next-button'; import PlaylistPreviousButton from './playlist-previous-button'; -import './playlist.scss'; +import './playlist-buttons.scss'; export { PlaylistNextButton, PlaylistPreviousButton }; diff --git a/src/components/playlist/components/playlist.scss b/src/plugins/playlist/ui/components/playlist-buttons.scss similarity index 100% rename from src/components/playlist/components/playlist.scss rename to src/plugins/playlist/ui/components/playlist-buttons.scss diff --git a/src/components/playlist/components/playlist-next-button.js b/src/plugins/playlist/ui/components/playlist-next-button.js similarity index 100% rename from src/components/playlist/components/playlist-next-button.js rename to src/plugins/playlist/ui/components/playlist-next-button.js diff --git a/src/components/playlist/components/playlist-previous-button.js b/src/plugins/playlist/ui/components/playlist-previous-button.js similarity index 100% rename from src/components/playlist/components/playlist-previous-button.js rename to src/plugins/playlist/ui/components/playlist-previous-button.js diff --git a/src/components/playlist/components/upcoming-video-overlay.js b/src/plugins/playlist/ui/components/upcoming-video-overlay.js similarity index 96% rename from src/components/playlist/components/upcoming-video-overlay.js rename to src/plugins/playlist/ui/components/upcoming-video-overlay.js index 434c27ff..0b63b55a 100644 --- a/src/components/playlist/components/upcoming-video-overlay.js +++ b/src/plugins/playlist/ui/components/upcoming-video-overlay.js @@ -1,6 +1,6 @@ import videojs from 'video.js'; import './upcoming-video-overlay.scss'; -import { PLAYER_EVENT } from '../../../utils/consts'; +import { PLAYER_EVENT } from 'utils/consts'; // support VJS5 & VJS6 at the same time const dom = videojs.dom || videojs; @@ -76,7 +76,7 @@ class UpcomingVideoOverlay extends ClickableComponent { this.setTitle(source); - content.el().style.backgroundImage = `url(${this._source.poster().url({ transformation })})`; + content.el().style.backgroundImage = `url("${this._source.poster().url({ transformation })}")`; } handleClick() { diff --git a/src/components/playlist/components/upcoming-video-overlay.scss b/src/plugins/playlist/ui/components/upcoming-video-overlay.scss similarity index 87% rename from src/components/playlist/components/upcoming-video-overlay.scss rename to src/plugins/playlist/ui/components/upcoming-video-overlay.scss index baa7b6fc..5f8a0f83 100644 --- a/src/components/playlist/components/upcoming-video-overlay.scss +++ b/src/plugins/playlist/ui/components/upcoming-video-overlay.scss @@ -1,13 +1,10 @@ -@import '../../../assets/styles/mixins/aspect-ratio'; $upcoming-video-max-width: 30em; -// $_upcoming-video-transition: visibility 1s, opacity 1s, background 1.1s 0s step-end; -$_upcoming-video-transition: visibility 0.4s, opacity 0.4s; +$_upcoming-video-transition: visibility 0.2s, opacity 0.2s; .vjs-upcoming-video { opacity: 0; transition: bottom 0.1s, $_upcoming-video-transition; - @include aspect-ratio(16, 9); visibility: hidden; position: absolute; @@ -18,6 +15,7 @@ $_upcoming-video-transition: visibility 0.4s, opacity 0.4s; border: 1px solid #E8E8E9; .upcoming-video-overlay { + aspect-ratio: 16/9; background-size: cover; cursor: pointer; diff --git a/src/components/playlist/layout/playlist-layout-custom.js b/src/plugins/playlist/ui/layout/playlist-layout-custom.js similarity index 100% rename from src/components/playlist/layout/playlist-layout-custom.js rename to src/plugins/playlist/ui/layout/playlist-layout-custom.js diff --git a/src/components/playlist/layout/playlist-layout-horizontal.js b/src/plugins/playlist/ui/layout/playlist-layout-horizontal.js similarity index 100% rename from src/components/playlist/layout/playlist-layout-horizontal.js rename to src/plugins/playlist/ui/layout/playlist-layout-horizontal.js diff --git a/src/components/playlist/layout/playlist-layout-vertical.js b/src/plugins/playlist/ui/layout/playlist-layout-vertical.js similarity index 100% rename from src/components/playlist/layout/playlist-layout-vertical.js rename to src/plugins/playlist/ui/layout/playlist-layout-vertical.js diff --git a/src/components/playlist/layout/playlist-layout.js b/src/plugins/playlist/ui/layout/playlist-layout.js similarity index 94% rename from src/components/playlist/layout/playlist-layout.js rename to src/plugins/playlist/ui/layout/playlist-layout.js index bfa86187..ecd3239b 100644 --- a/src/components/playlist/layout/playlist-layout.js +++ b/src/plugins/playlist/ui/layout/playlist-layout.js @@ -1,10 +1,10 @@ import videojs from 'video.js'; -import { wrap } from '../../../utils/dom'; +import { PLAYER_EVENT } from 'utils/consts'; +import { wrap } from 'utils/dom'; import { skinClassPrefix, playerClassPrefix -} from '../../../utils/css-prefix'; -import { PLAYER_EVENT } from '../../../utils/consts'; +} from 'utils/css-prefix'; const dom = videojs.dom || videojs; const Component = videojs.getComponent('Component'); diff --git a/src/components/playlist/panel/playlist-panel-item.js b/src/plugins/playlist/ui/panel/playlist-panel-item.js similarity index 100% rename from src/components/playlist/panel/playlist-panel-item.js rename to src/plugins/playlist/ui/panel/playlist-panel-item.js diff --git a/src/components/playlist/panel/playlist-panel.js b/src/plugins/playlist/ui/panel/playlist-panel.js similarity index 94% rename from src/components/playlist/panel/playlist-panel.js rename to src/plugins/playlist/ui/panel/playlist-panel.js index 69d46f2f..3bc47498 100644 --- a/src/components/playlist/panel/playlist-panel.js +++ b/src/plugins/playlist/ui/panel/playlist-panel.js @@ -1,7 +1,6 @@ import videojs from 'video.js'; -import 'assets/styles/components/playlist.scss'; import PlaylistPanelItem from './playlist-panel-item'; -import { PLAYER_EVENT } from '../../../utils/consts'; +import { PLAYER_EVENT } from 'utils/consts'; const Component = videojs.getComponent('Component'); diff --git a/src/components/playlist/playlist-widget.js b/src/plugins/playlist/ui/playlist-widget.js similarity index 97% rename from src/components/playlist/playlist-widget.js rename to src/plugins/playlist/ui/playlist-widget.js index 7c5f7772..8c3a4ff2 100644 --- a/src/components/playlist/playlist-widget.js +++ b/src/plugins/playlist/ui/playlist-widget.js @@ -1,10 +1,9 @@ import videojs from 'video.js'; +import { PLAYER_EVENT } from 'utils/consts'; import PlaylistLayoutHorizontal from './layout/playlist-layout-horizontal'; import PlaylistLayoutVertical from './layout/playlist-layout-vertical'; import PlaylistLayoutCustom from './layout/playlist-layout-custom'; import { PLAYLIST_DEFAULTS_OPTIONS } from './playlist.const'; -import { PLAYER_EVENT } from '../../utils/consts'; - const modifyOptions = (player, opt) => { const options = { ...PLAYLIST_DEFAULTS_OPTIONS, ...opt }; @@ -32,7 +31,6 @@ const modifyOptions = (player, opt) => { return options; }; - class PlaylistWidget { constructor(player, options = {}) { options = modifyOptions(player, options); @@ -46,7 +44,7 @@ class PlaylistWidget { player.on(PLAYER_EVENT.FLUID, fluidHandler); - this.options = (options) => { + this.options = options => { if (!options) { return this.options_; } @@ -54,7 +52,6 @@ class PlaylistWidget { this.options_ = videojs.obj.merge(this.options_, options); player.trigger('playlistwidgetoption', this.options_.playlistWidget); return this.options_; - }; this.dispose = () => { @@ -116,5 +113,4 @@ class PlaylistWidget { } } - export default PlaylistWidget; diff --git a/src/components/playlist/playlist.const.js b/src/plugins/playlist/ui/playlist.const.js similarity index 100% rename from src/components/playlist/playlist.const.js rename to src/plugins/playlist/ui/playlist.const.js diff --git a/src/components/playlist/playlist.js b/src/plugins/playlist/ui/playlist.js similarity index 92% rename from src/components/playlist/playlist.js rename to src/plugins/playlist/ui/playlist.js index db19a56b..ab8f0be0 100644 --- a/src/components/playlist/playlist.js +++ b/src/plugins/playlist/ui/playlist.js @@ -1,8 +1,8 @@ import VideoSource from 'plugins/cloudinary/models/video-source/video-source'; import { isInteger } from 'utils/type-inference'; -import 'components/playlist/components/upcoming-video-overlay'; -import 'components/playlist/components/playlist'; +import './components/upcoming-video-overlay'; +import './components/playlist-buttons'; import { DEFAULT_AUTO_ADVANCE, @@ -10,6 +10,8 @@ import { UPCOMING_VIDEO_TRANSITION } from './playlist.const'; +import './playlist.scss'; + class Playlist { constructor(context, sources = [], { repeat = false, autoAdvance = false, presentUpcoming = false } = {}) { @@ -21,6 +23,8 @@ class Playlist { this._autoAdvance = null; this._presentUpcoming = null; + this.addUiComponents(); + this.resetState = () => { this.repeat(repeat); this.autoAdvance(autoAdvance); @@ -40,6 +44,15 @@ class Playlist { return this._context.player; } + addUiComponents() { + const controlBar = this.player().getChild('ControlBar'); + const children = controlBar.children(); + controlBar.addChild('playlistPreviousButton', {}, children.findIndex(c => c.name_ === 'PlayToggle')); + controlBar.addChild('playlistNextButton', {}, children.findIndex(c => c.name_ === 'PlayToggle') + 1); + + this.player().addChild('upcomingVideoOverlay'); + } + presentUpcoming(delay) { this._presentUpcoming = this._presentUpcoming || {}; @@ -410,6 +423,10 @@ class Playlist { return this.playAtIndex(previousIndex); } + + playlistByTag = (tag, options = {}) => { + return this.player().sourcesByTag(tag, options).then(sources => this.player().playlist(sources, options)); + }; } export default Playlist; diff --git a/src/plugins/playlist/ui/playlist.scss b/src/plugins/playlist/ui/playlist.scss new file mode 100644 index 00000000..f099c233 --- /dev/null +++ b/src/plugins/playlist/ui/playlist.scss @@ -0,0 +1,204 @@ +.cld-plw-horizontal { + .cld-plw-col-list { + padding-top: 14.0625%; + position: relative; + } + + .cld-plw-panel { + overflow: auto; + position: absolute; + top: 0; + bottom: 0; + right: 0; + left: 0; + display: flex; + flex-direction: row; + } + + .cld-plw-panel-item { + background-image: none; + min-width: 25%; + max-width: 25%; + + &:first-child { + margin-left: 0; + } + + &:last-child { + margin-right: 0; + } + } +} + +.cld-plw-vertical { + display: flex; + flex-direction: row; + + &.cld-plw-layout { + > div:first-child { + flex: 1; + } + } + + .cld-plw-col-list { + display: flex; + flex-direction: column; + min-width: 20%; + } + + .cld-plw-panel { + height: 100%; + overflow: auto; + } + + .cld-plw-panel-item { + height: 25%; + + &:last-child { + margin-bottom: 0; + } + } +} + +.cld-plw-item-title-next { + font-weight: bold; +} + +.cld-plw-item-info-wrap { + position: absolute; + bottom: 0; + left: 0; + padding: 5% 7%; + width: 100%; + transition: color 0.25s; + display: flex; + flex-direction: row; + justify-content: space-between; + box-sizing: border-box; + + .cld-video-player-skin-dark & { + text-shadow: 1px 1px 1px rgba(#000, 0.3); + + @media only screen and (max-width: 768px) { + background: var(--color-base); + } + } +} + +.cld-plw-item-title { + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; +} + +.cld-plw-item-duration { + margin-left: 10px; +} + +/* Default Playlist Layout */ + +.cld-plw-layout { + &.cld-plw-layout-fluid { + width: 100%; + } + + &.cld-plw-vertical { + flex-direction: row; + } + + &.cld-plw-horizontal { + flex-direction: column; + } +} + +.cld-plw-custom { + padding: 4px; + .cld-plw-panel-item { + background: none !important; + margin: 4px 0; + &:first-child { + margin-top: 0; + } + + &:last-child { + margin-bottom: 0; + } + + img { + display: block; + width: 150px; + } + + .cld-plw-item-info-wrap { + left: 160px; + } + + .cld-plw-item-title-next { + display: block; + } + } +} + +@media only screen and (max-width: 768px) { + .cld-plw-layout { + &.cld-plw-vertical, + &.cld-plw-horizontal { + flex-direction: column; + + .cld-plw-panel { + flex-direction: column; + } + + .cld-plw-col-list { + width: inherit !important; + height: inherit !important; + max-height: 340px; + } + + .cld-plw-panel-item { + background: none !important; + + img { + display: block; + width: 150px; + height: 84px; + } + + .cld-plw-item-info-wrap { + left: 150px; + padding: 15px 10px; + top: 0; + right: 0; + width: auto; + } + + .cld-plw-item-title-next { + display: block; + } + + .cld-plw-item-title-curr { + display: block; + } + + .cld-plw-item-title { + white-space: normal; + } + } + } + + &.cld-plw-horizontal { + .cld-plw-col-list { + padding-top: 0; + overflow: auto; + } + + .cld-plw-panel { + position: initial; + } + + .cld-plw-panel-item { + max-width: none; + } + } + } +} diff --git a/src/components/playlist/thumbnail/thumbnail.js b/src/plugins/playlist/ui/thumbnail/thumbnail.js similarity index 95% rename from src/components/playlist/thumbnail/thumbnail.js rename to src/plugins/playlist/ui/thumbnail/thumbnail.js index 395c1df0..9eb652a1 100644 --- a/src/components/playlist/thumbnail/thumbnail.js +++ b/src/plugins/playlist/ui/thumbnail/thumbnail.js @@ -1,5 +1,5 @@ import videojs from 'video.js'; -import '../../../assets/styles/components/thumbnail.scss'; +import './thumbnail.scss'; // Get the ClickableComponent base class from Video.js const ClickableComponent = videojs.getComponent('ClickableComponent'); diff --git a/src/assets/styles/components/thumbnail.scss b/src/plugins/playlist/ui/thumbnail/thumbnail.scss similarity index 100% rename from src/assets/styles/components/thumbnail.scss rename to src/plugins/playlist/ui/thumbnail/thumbnail.scss diff --git a/src/plugins/shoppable-plugin/index.js b/src/plugins/shoppable-plugin/index.js new file mode 100644 index 00000000..65c36757 --- /dev/null +++ b/src/plugins/shoppable-plugin/index.js @@ -0,0 +1,6 @@ +export default async function lazyShoppablePlugin(player, options) { + + const { default: ShoppableWidget } = await import(/* webpackChunkName: "shoppable" */ '../../components/shoppable-bar/shoppable-widget'); + new ShoppableWidget(player, options.shoppable).init(); + +} diff --git a/src/utils/consts.js b/src/utils/consts.js index 2ebc4b0a..5521e407 100644 --- a/src/utils/consts.js +++ b/src/utils/consts.js @@ -1,4 +1,5 @@ export const PLAYER_EVENT = { + READY: 'ready', PLAY: 'play', PLAYING: 'playing', PAUSE: 'pause', diff --git a/src/utils/get-analytics-player-options.js b/src/utils/get-analytics-player-options.js index d9b8016f..ef8a41a0 100644 --- a/src/utils/get-analytics-player-options.js +++ b/src/utils/get-analytics-player-options.js @@ -28,8 +28,18 @@ const getSourceOptions = (sourceOptions = {}) => ({ sourceInfoSubtitle: sourceOptions.info.subtitle, sourceInfoDescription: sourceOptions.info.description } : {}), - textTracks: hasConfig(sourceOptions.textTracks), - pacedTextTracks: hasConfig(sourceOptions.textTracks) && JSON.stringify(sourceOptions.textTracks || {}).includes('"maxWords":') + ...(sourceOptions.textTracks ? { + textTracks: hasConfig(sourceOptions.textTracks), + pacedTextTracks: hasConfig(sourceOptions.textTracks) && JSON.stringify(sourceOptions.textTracks || {}).includes('"maxWords":'), + ...(sourceOptions.textTracks.options ? { + styledTextTracksTheme: sourceOptions.textTracks.options.theme, + styledTextTracksFont: sourceOptions.textTracks.options.fontFace, + styledTextTracksFontSize: sourceOptions.textTracks.options.fontSize, + styledTextTracksGravity: sourceOptions.textTracks.options.gravity, + styledTextTracksBox: hasConfig(sourceOptions.textTracks.options.box), + styledTextTracksStyle: hasConfig(sourceOptions.textTracks.options.style) + } : {}) + } : {}) }); const getAdsOptions = (adsOptions = {}) => ({ @@ -73,6 +83,8 @@ export const getAnalyticsFromPlayerOptions = (playerOptions) => filterDefaultsAn videoTimeout: playerOptions.videoTimeout, seekThumbnails: playerOptions.seekThumbnails, showJumpControls: playerOptions.showJumpControls, + chaptersButton: playerOptions.chaptersButton, + pictureInPictureToggle: playerOptions.pictureInPictureToggle, showLogo: playerOptions.showLogo, skin: playerOptions.skin, videoJS: hasConfig(playerOptions.videoJS), diff --git a/src/validators/validators.js b/src/validators/validators.js index 4bdc01ed..ab39ae95 100644 --- a/src/validators/validators.js +++ b/src/validators/validators.js @@ -2,7 +2,7 @@ import { ADS_IN_PLAYLIST, AUTO_PLAY_MODE, FLOATING_TO } from '../video-player.co import { INTERACTION_AREAS_TEMPLATE, INTERACTION_AREAS_THEME -} from '../components/interaction-area/interaction-area.const'; +} from '../plugins/interaction-areas/interaction-areas.const'; import { validator } from './validators-types'; export const playerValidators = { @@ -43,6 +43,8 @@ export const playerValidators = { hideContextMenu: validator.isBoolean, playedEventPercents: validator.isArrayOfNumbers, showJumpControls: validator.isBoolean, + chaptersButton: validator.isBoolean, + pictureInPictureToggle: validator.isBoolean, seekThumbnails: validator.isBoolean, aiHighlightsGraph: validator.isBoolean, floatingWhenNotVisible: validator.isString(FLOATING_TO), diff --git a/src/video-player-profile.js b/src/video-player-profile.js new file mode 100644 index 00000000..35bc423a --- /dev/null +++ b/src/video-player-profile.js @@ -0,0 +1,37 @@ +import VideoPlayer from './video-player'; + +export const getProfile = async (cloudName, profile) => { + const { defaultProfiles } = await import(/* webpackChunkName: "defaultProfiles" */ './config/profiles'); + + if (Object.keys(defaultProfiles).includes(profile)) { + return defaultProfiles[profile]; + } + + return await fetch(profile, { method: 'GET' }).then(res => res.json()); +}; + +const videoPlayerProfile = async (elem, initOptions, ready) => { + if (!initOptions.profile) { + throw new Error('VideoPlayerProfile method requires "profile" property'); + } + + try { + const profileOptions = await getProfile(initOptions.cloud_name, initOptions.profile); + const options = Object.assign({}, profileOptions.playerOptions, initOptions); + const videoPlayer = new VideoPlayer(elem, options, ready); + + const nativeVideoPlayerSourceMethod = videoPlayer.source; + videoPlayer.source = (id, options) => { + const extendedOptions = Object.assign({}, profileOptions.sourceOptions, options); + return nativeVideoPlayerSourceMethod.call(videoPlayer, id, extendedOptions); + }; + + return videoPlayer; + } catch (e) { + const videoPlayer = new VideoPlayer(elem, initOptions); + videoPlayer.videojs.error('Invalid profile'); + throw e; + } +}; + +export default videoPlayerProfile; diff --git a/src/video-player.const.js b/src/video-player.const.js index b195c8ed..f621bcde 100644 --- a/src/video-player.const.js +++ b/src/video-player.const.js @@ -27,6 +27,8 @@ export const PLAYER_PARAMS = CLOUDINARY_PARAMS.concat([ 'floatingWhenNotVisible', 'ads', 'showJumpControls', + 'chaptersButton', + 'pictureInPictureToggle', 'textTracks', 'qualitySelector', 'fetchErrorUsingGet', diff --git a/src/video-player.js b/src/video-player.js index 9a8bc699..5e6357fe 100644 --- a/src/video-player.js +++ b/src/video-player.js @@ -7,9 +7,8 @@ import Utils from './utils'; import defaults from './config/defaults'; import Eventable from './mixins/eventable'; import ExtendedEvents from './extended-events'; -import PlaylistWidget from './components/playlist/playlist-widget'; import VideoSource from './plugins/cloudinary/models/video-source/video-source'; -import { isFunction, isString, isPlainObject } from './utils/type-inference'; +import { isFunction, isString } from './utils/type-inference'; import { extractOptions, getResolveVideoElement, @@ -24,7 +23,6 @@ import { getAnalyticsFromPlayerOptions } from './utils/get-analytics-player-opti import { extendCloudinaryConfig, normalizeOptions, isRawUrl } from './plugins/cloudinary/common'; // #if (!process.env.WEBPACK_BUILD_LIGHT) import qualitySelector from './components/qualitySelector/qualitySelector.js'; -import { interactionAreaService } from './components/interaction-area/interaction-area.service'; // #endif const INTERNAL_ANALYTICS_URL = 'https://analytics-api-s.cloudinary.com'; @@ -61,7 +59,6 @@ class VideoPlayer extends Utils.mixin(Eventable) { constructor(elem, initOptions, ready) { super(); - this._playlistWidget = null; this.nbCalls = 0; this.videoElement = getResolveVideoElement(elem); @@ -99,14 +96,10 @@ class VideoPlayer extends Utils.mixin(Eventable) { this.fluid(this.playerOptions.fluid); } - // #if (!process.env.WEBPACK_BUILD_LIGHT) - this.interactionArea = interactionAreaService(this, this.playerOptions, this._videojsOptions); - // #endif - this._setCssClasses(); this._initPlugins(); - this._initPlaylistWidget(); this._initJumpButtons(); + this._initPictureInPicture(); this._setVideoJsListeners(ready); } @@ -173,6 +166,7 @@ class VideoPlayer extends Utils.mixin(Eventable) { this.videojs.on(PLAYER_EVENT.PLAY, this._clearTimeOut); this.videojs.on(PLAYER_EVENT.CAN_PLAY_THROUGH, this._clearTimeOut); + this.videojs.on(PLAYER_EVENT.CLD_SOURCE_CHANGED, this._onSourceChange.bind(this)); this.videojs.ready(() => { this._onReady(); @@ -180,10 +174,6 @@ class VideoPlayer extends Utils.mixin(Eventable) { if (ready) { ready(this); } - - // #if (!process.env.WEBPACK_BUILD_LIGHT) - this.interactionArea.init(); - // #endif }); } @@ -204,6 +194,7 @@ class VideoPlayer extends Utils.mixin(Eventable) { this._initHighlightsGraph(); this._initSeekThumbs(); this._initChapters(); + this._initInteractionAreas(); } _isFullScreen() { @@ -267,8 +258,9 @@ class VideoPlayer extends Utils.mixin(Eventable) { const transformation = Utils.assign({}, source.transformation()); - if (transformation && transformation.streaming_profile) { + if (transformation) { delete transformation.streaming_profile; + delete transformation.video_codec; } transformation.flags = transformation.flags || []; @@ -321,6 +313,9 @@ class VideoPlayer extends Utils.mixin(Eventable) { } _initChapters() { + if (!this.playerOptions.chaptersButton && this.videojs.controlBar) { + this.videojs.controlBar.removeChild('chaptersButton'); + } this.videojs.on(PLAYER_EVENT.CLD_SOURCE_CHANGED, (e, { source }) => { if ((!isEmpty(source._chapters) || source._chapters === true) && this.videojs.chapters) { isFunction(this.videojs.chapters) @@ -330,13 +325,21 @@ class VideoPlayer extends Utils.mixin(Eventable) { }); } + _initInteractionAreas() { + this.videojs.on(PLAYER_EVENT.READY, async () => { + if (this.options.videojsOptions.interactionDisplay && this.videojs.interactionAreas) { + this.videojs.interactionAreas(this, this.playerOptions, this._videojsOptions); + } + }); + } + _initColors () { this.videojs.colors(this.playerOptions.colors ? { colors: this.playerOptions.colors } : {}); } // #if (!process.env.WEBPACK_BUILD_LIGHT) _initQualitySelector() { - if (this._videojsOptions.controlBar && this.playerOptions.qualitySelector !== false) { + if (this.videojs.controlBar && this.playerOptions.qualitySelector !== false) { this.videojs.httpSourceSelector({ default: 'auto' }); this.videojs.on(PLAYER_EVENT.LOADED_METADATA, () => { @@ -370,6 +373,12 @@ class VideoPlayer extends Utils.mixin(Eventable) { } } + _initPictureInPicture() { + if (!this.playerOptions.pictureInPictureToggle && this.videojs.controlBar) { + this.videojs.controlBar.removeChild('pictureInPictureToggle'); + } + } + _initCloudinary() { const { cloudinaryConfig } = this.playerOptions.cloudinary; cloudinaryConfig.chainTarget = this; @@ -426,44 +435,6 @@ class VideoPlayer extends Utils.mixin(Eventable) { return false; } - _initPlaylistWidget () { - this.videojs.on(PLAYER_EVENT.PLAYLIST_CREATED, () => { - - if (this._playlistWidget) { - this._playlistWidget.dispose(); - } - const plwOptions = this.playerOptions.playlistWidget; - - if (isPlainObject(plwOptions)) { - if (this.playerOptions.fluid) { - plwOptions.fluid = true; - } - - if (this.playerOptions.cloudinary.fontFace) { - plwOptions.fontFace = this.playerOptions.cloudinary.fontFace; - } - - this._playlistWidget = new PlaylistWidget(this.videojs, plwOptions); - } - }); - } - - playlistWidget(options) { - if (!options && !this._playlistWidget) { - return false; - } - - if (!options && this._playlistWidget) { - return this._playlistWidget; - } - - if (isPlainObject(options)) { - this._playlistWidget.options(options); - } - - return this._playlistWidget; - } - _initAutoplay() { const autoplayMode = this.playerOptions.autoplayMode; @@ -502,6 +473,13 @@ class VideoPlayer extends Utils.mixin(Eventable) { } } + _onSourceChange() { + this._sendInternalAnalytics(); + // #if (!process.env.WEBPACK_BUILD_LIGHT) + this._initQualitySelector(); + // #endif + } + _setExtendedEvents() { const events = []; if (this.playerOptions.playedEventPercents) { @@ -566,19 +544,14 @@ class VideoPlayer extends Utils.mixin(Eventable) { return this.videojs.cloudinary.source(publicId, options); } - // Interactive plugin - available in full (not light) build only - if (this.videojs.interactive) { - this.videojs.interactive(this.videojs, options); + if (options.shoppable && this.videojs.shoppable) { + this.videojs.shoppable(this.videojs, options); } if (VideoPlayer.allowUsageReport()) { options.usageReport = true; } - // #if (!process.env.WEBPACK_BUILD_LIGHT) - this._initQualitySelector(); - // #endif - clearTimeout(this.reTryId); this.nbCalls = 0; const maxTries = this.videojs.options_.maxTries || 3; @@ -595,28 +568,32 @@ class VideoPlayer extends Utils.mixin(Eventable) { skin(name) { if (name !== undefined && isString(name)) { Utils.setSkinClassPrefix(this.videojs, name); - - const playlistWidget = this.playlistWidget(); - - if (playlistWidget) { - playlistWidget.setSkin(); - } } return Utils.skinClassPrefix(this.videojs); } playlist(sources, options = {}) { - // #if (!process.env.WEBPACK_BUILD_LIGHT) - this._initQualitySelector(); - // #endif - this._sendInternalAnalytics(); - return this.videojs.cloudinary.playlist(sources, options); + options = Utils.assign({}, options, { playlistWidget: this.playerOptions.playlistWidget }); + + this.videojs.one(PLAYER_EVENT.READY, async () => { + const playlistPlugin = await this.videojs.playlist(options); + playlistPlugin(sources, options); + }); + + return this.videojs.cloudinary.playlist ? this.videojs.cloudinary.playlist(sources, options) : this; } playlistByTag(tag, options = {}) { - this._sendInternalAnalytics(); - return this.videojs.cloudinary.playlistByTag(tag, options); + options = Utils.assign({}, options, { playlistWidget: this.playerOptions.playlistWidget }); + + return new Promise((resolve) => { + this.videojs.one(PLAYER_EVENT.READY, async () => { + const playlistPlugin = await this.videojs.playlist(options); + playlistPlugin(await this.sourcesByTag(tag, options), options); + resolve(this); + }); + }); } sourcesByTag(tag, options = {}) { diff --git a/src/video-player.utils.js b/src/video-player.utils.js index 092f20cc..b4dbbf4d 100644 --- a/src/video-player.utils.js +++ b/src/video-player.utils.js @@ -110,18 +110,16 @@ export const overrideDefaultVideojsComponents = () => { // Add TitleBar as default children.push('titleBar'); - children.push('upcomingVideoOverlay'); - children.push('recommendationsOverlay'); const ControlBar = videojs.getComponent('ControlBar'); if (ControlBar) { children = ControlBar.prototype.options_.children; - // Add space instead of the progress control (which we deattached from the controlBar, and absolutely positioned it above it) + // Add space instead of the progress control (which we detached from the controlBar, and absolutely positioned it above it) // Also add a blank div underneath the progress control to stop bubbling up pointer events. children.splice(children.indexOf('progressControl'), 0, 'spacer', 'progressControlEventsBlocker'); - // Add 'play-previous' and 'play-next' buttons around the 'play-toggle' - children.splice(children.indexOf('playToggle'), 1, 'playlistPreviousButton', 'playToggle', 'JumpBackButton', 'JumpForwardButton', 'playlistNextButton'); + // Add skip buttons around the 'play-toggle' + children.splice(children.indexOf('playToggle'), 1, 'playToggle', 'JumpBackButton', 'JumpForwardButton'); // Position the 'logo-button' button last children.push('logoButton'); diff --git a/src/video-profile-export.es.js b/src/video-profile-export.es.js new file mode 100644 index 00000000..8c86de90 --- /dev/null +++ b/src/video-profile-export.es.js @@ -0,0 +1 @@ +export { videoPlayerWithProfile as default } from './index.js'; diff --git a/test/unit/videoSource.test.js b/test/unit/videoSource.test.js index 8bdd6333..37469bf4 100644 --- a/test/unit/videoSource.test.js +++ b/test/unit/videoSource.test.js @@ -361,13 +361,14 @@ describe('test isCodecAlreadyExist method', () => { }); const srcs = source.generateSources(); - expect(srcs[0].src).toEqual('http://res.cloudinary.com/demo/video/upload/vc_vp9/sea_turtle.webm'); + expect(srcs[0].src).toEqual('http://res.cloudinary.com/demo/video/upload/f_auto:video/sea_turtle'); }); - it('check if codec has NOT bee added to the ur twice', () => { + it('with codec and sourceType', () => { const source = new VideoSource('sea_turtle', { cloudinaryConfig: cld, + sourceTypes: ['webm'], transformation: { video_codec: 'vp9' } @@ -377,6 +378,19 @@ describe('test isCodecAlreadyExist method', () => { expect(srcs[0].src).toEqual('http://res.cloudinary.com/demo/video/upload/vc_vp9/sea_turtle.webm'); }); + it('codec with no sourceType', () => { + + const source = new VideoSource('sea_turtle', { + cloudinaryConfig: cld, + transformation: { + video_codec: 'vp9' + } + }); + + const srcs = source.generateSources(); + expect(srcs[0].src).toEqual('http://res.cloudinary.com/demo/video/upload/vc_vp9/f_auto:video/sea_turtle'); + }); + it('check if codec has been changed', () => { const source = new VideoSource('sea_turtle', { @@ -387,11 +401,11 @@ describe('test isCodecAlreadyExist method', () => { }); const srcs = source.generateSources(); - expect(srcs[0].src).toEqual('http://res.cloudinary.com/demo/video/upload/vc_h265/sea_turtle.webm'); + expect(srcs[0].src).toEqual('http://res.cloudinary.com/demo/video/upload/vc_h265/f_auto:video/sea_turtle'); }); - it('check if codec has been NOT add twice using raw_transformation', () => { + it('raw_transformation codec with no sourceType', () => { const source = new VideoSource('sea_turtle', { cloudinaryConfig: cld, @@ -399,7 +413,7 @@ describe('test isCodecAlreadyExist method', () => { }); const srcs = source.generateSources(); - expect(srcs[0].src).toEqual('http://res.cloudinary.com/demo/video/upload/vc_vp9/sea_turtle.webm'); + expect(srcs[0].src).toEqual('http://res.cloudinary.com/demo/video/upload/f_auto:video/vc_vp9/sea_turtle'); }); it('check if codec has been change using raw_transformation', () => { @@ -410,7 +424,7 @@ describe('test isCodecAlreadyExist method', () => { }); const srcs = source.generateSources(); - expect(srcs[0].src).toEqual('http://res.cloudinary.com/demo/video/upload/h265/sea_turtle.webm'); + expect(srcs[0].src).toEqual('http://res.cloudinary.com/demo/video/upload/f_auto:video/h265/sea_turtle'); }); @@ -424,7 +438,7 @@ describe('test isCodecAlreadyExist method', () => { }); const srcs = source.generateSources(); - expect(srcs[0].src).toEqual('http://res.cloudinary.com/demo/video/upload/w_400/vc_vp9/sea_turtle.webm'); + expect(srcs[0].src).toEqual('http://res.cloudinary.com/demo/video/upload/w_400/f_auto:video/sea_turtle'); }); it('check array of transformations with codec', () => { @@ -437,12 +451,8 @@ describe('test isCodecAlreadyExist method', () => { }); const srcs = source.generateSources(); - expect(srcs[0].src).toEqual('http://res.cloudinary.com/demo/video/upload/vc_h265/sea_turtle.webm'); + expect(srcs[0].src).toEqual('http://res.cloudinary.com/demo/video/upload/vc_h265/f_auto:video/sea_turtle'); }); - }); - }); - - diff --git a/webpack/common.config.js b/webpack/common.config.js index a07dbfed..9c83b33c 100644 --- a/webpack/common.config.js +++ b/webpack/common.config.js @@ -59,10 +59,6 @@ const webpackConfig = { 'eslint-loader' ] }, - { - test: /\.json$/, - use: 'json-loader' - }, { test: /\.scss$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'] diff --git a/webpack/es6.config.js b/webpack/es6.config.js index dcbd3b29..cb9938e9 100644 --- a/webpack/es6.config.js +++ b/webpack/es6.config.js @@ -9,7 +9,8 @@ module.exports = merge(webpackCommon, { entry: { 'cld-video-player': './index.all.js', - 'videoPlayer': './index.es.js' + 'videoPlayer': './index.es.js', + 'videoPlayerWithProfile': './video-profile-export.es.js' }, output: {