H5P Standalone

repository·master·Indexed 43 days ago

https://github.com/tunapanda/h5p-standalone

A player that allows developers to display H5P interactive content directly in their applications as standalone frontend code without requiring a dedicated H5P server. It supports xAPI event listening, manual or AJAX-based user state management, and custom library loading via customJs.

Tokens
7.7K
Snippets
23
Records
28
Agent score
54%

What's inside H5P Standalone

  1. Prepare H5P Content for the Player

    master

    H5P files (.h5p) are zip archives. The player cannot use the .h5p file directly; you must extract the contents of the .h5p file into a folder within your project. This folder path is then provided to the h5pJsonPath option.

    Warning on Missing Libraries: Content exported from h5p.org may lack necessary libraries because the platform assumes they are already present on the server. If content fails to load, you may need to manually download the missing libraries or use H5P content that explicitly bundles all required libraries.

  2. Install H5P Standalone via CDN (Manual Installation)

    master

    For projects without a build pipeline, you can include the player directly in your HTML using a CDN like jsDelivr.

    Important: While you can use @latest, it is recommended to pin to a specific version (e.g., @3.9.0) in production to prevent breaking changes from unexpected updates.

    <!-- Include these in your HTML -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/h5p-standalone@latest/dist/styles/h5p.css">
    <script src="https://cdn.jsdelivr.net/npm/h5p-standalone@latest/dist/main.bundle.js" charset="UTF-8"></script>
    
    <script>
      document.addEventListener('DOMContentLoaded', function () {
        const options = {
          h5pJsonPath: '/path/to/your/h5p-folder', // Path to your self-hosted H5P content directory
          frameJs: 'https://cdn.jsdelivr.net/npm/h5p-standalone@latest/dist/frame.bundle.js',
          frameCss: 'https://cdn.jsdelivr.net/npm/h5p-standalone@latest/dist/styles/h5p.css',
        };
    
        const el = document.getElementById('h5p-container');
        // H5PStandalone is globally available via the script tag
        new H5PStandalone.H5P(el, options);
      });
    </script>
  3. Install H5P Standalone via Package Manager

    master

    For projects using a JavaScript module bundler (React, Vue, Svelte, etc.), install the package using your preferred package manager.

    # pnpm
    pnpm install h5p-standalone
    
    # yarn
    yarn add h5p-standalone
    
    # npm
    npm install h5p-standalone
  4. Manage H5P user state manually

    master

    You can manage user progress (saving and loading state) manually by providing the contentUserData option during initialization and implementing your own storage logic.

    To restore state, pass an array containing an object with dataType: 'state' and the previousState (as a JSON string) to the contentUserData option.

    To save state, access the current state via window.H5PIntegration.contents["cid-${contentId}"].contentUserData and implement your own persistence logic. It is recommended to call your save function periodically using a setInterval synchronized with the saveFreq option.

    function retrieveSavedContentState(contentId){
     // 👉 your implementation that returns the state for this content (and of course for this user)
    }
    
    function saveContentState(contentId){
     const currentState = window.H5PIntegration.contents[`cid-${contentId}`].contentUserData;
      //👉 you can call your logic to save this state somewhere
    }
    
    const CONTENT_ID = 'some-content-unique-id-xxxxx';
    const previousStateJSON = await retrieveSavedContentState(CONTENT_ID)
    
    const options = {
    	id: CONTENT_ID,
    	h5pJsonPath: '/content/course-presentation-one',
    	frameJs: '/dist/frame.bundle.js',
    	frameCss: '/dist/styles/h5p.css',
    	saveFreq: 100, // required
    	contentUserData: [
      {
        dataType: 'state',
        previousState: previousStateJSON, // should be a json string
      }
      ]
    }
    
    await new H5PStandalone(el, options);
    
    // Call save function periodically based on saveFreq (converted to ms)
    setInterval(()=>saveContentState(CONTENT_ID), options.saveFreq * 1000);
  5. Extract H5P content for use with the player

    master

    An .h5p file is a zip archive. To use it with the h5p-standalone player:

    1. Rename the file extension from .h5p to .zip.
    2. Extract the contents into a folder within your project.
    3. Point the h5pJsonPath option to this folder.
  6. Build and test the project during development

    master

    If you are developing on the h5p-standalone repository, use the following commands:

    • Install dependencies: yarn install
    • Build distributable files: yarn build
    • Run Cypress tests: yarn test
    yarn install
    yarn build
    yarn test
  7. Configure H5P iframe resizer for embeds

    master

    To ensure the H5P iframe behaves correctly when embedded:

    1. Prompt Resizing: To make the iframe width resize promptly with the parent page, add style="width:100%" to the <iframe> tag.
    2. User Resizing: To allow users to use H5P's built-in resize handles, use the :w and :h placeholders for the width and height attributes.

    Example Embed Code:

    <iframe width=":w" height=":h" src="https://app.wikonnect.org/embed/YOUR-CONTENT-ID" frameBorder="0" scrolling="no" style="width:100%">
    </iframe>
  8. Render multiple H5P players on the same page

    master

    To render multiple H5P instances simultaneously, you must handle the initialization asynchronously. Since the constructor returns a Promise, use await or .then() to ensure each player is initialized correctly without blocking the others.

    Note: Ensure each player has a unique container element and appropriate configuration.

    import { H5P } from 'h5p-standalone';
    
    const player1Options = {
        h5pJsonPath: '/h5p/exercise-one',
        frameJs: '/assets/frame.bundle.js',
        frameCss: '/assets/styles/h5p.css',
    };
    
    const player2Options = {
        h5pJsonPath: '/h5p/exercise-two',
        frameJs: '/assets/frame.bundle.js',
        frameCss: '/assets/styles/h5p.css',
    };
    
    // Using top-level await to initialize multiple players
    await new H5P(document.getElementById('h5p-container-1'), player1Options);
    await new H5P(document.getElementById('h5p-container-2'), player2Options);
  9. Automate H5P user state management via AJAX

    master

    The player can automatically fetch and save user state using a backend endpoint. This requires configuring the ajax.contentUserDataUrl, saveFreq, and a user object.

    Backend Requirements

    Your server must implement the following at the contentUserDataUrl:

    • GET Request: Triggered when content loads. Return a JSON array of state objects, e.g., [{ "dataType": "state", "previousState": "{\"some\":\"json\"}" }]. Return [] if no state exists.
    • POST Request: Triggered based on saveFreq. The request body contains the user state data to be stored.

    URL Placeholders

    The player replaces placeholders in the contentUserDataUrl string before making requests:

    • :contentId: The unique ID of the content.
    • :dataType: The type of data (e.g., state).
    • :subContentId: The sub-content ID.

    Example Configuration

    const options = {
      h5pJsonPath: '/content/course-presentation-one',
      frameJs: '/dist/frame.bundle.js',
      frameCss: '/dist/styles/h5p.css',
      saveFreq: 100, // Time in seconds for autosaving
      ajax: {
        contentUserDataUrl: '/api/v1/h5p/history/users/123/content/:contentId'
      },
      user: {
        name: 'John Doe',
        mail: 'john@example.com' // Used to uniquely identify the user
      },
    };
    const options = {
      h5pJsonPath: '/content/course-presentation-one',
      frameJs: '/dist/frame.bundle.js',
      frameCss: '/dist/styles/h5p.css',
      saveFreq: 100,
      ajax: {
        contentUserDataUrl: '/api/v1/h5p/history/users/123/content/:contentId'
      },
      user: {
        name: 'John Doe',
        mail: 'john@example.com'
      },
    };
  10. Control H5P initialization timing

    master

    By default, H5PStandalone attempts to initialize the H5P content immediately after rendering the frame. If you need to perform manual setup or wait for other components before the H5P content starts, use the preventH5PInit option.

    When preventH5PInit is set to true, the player will render the frame but will not call window.H5P.init(). You can then manually trigger initialization by calling window.H5P.init().

    // Prevent automatic init
    new H5PStandalone(element, { preventH5PInit: true });
    
    // Later, manually initialize
    if (typeof (window as any).H5P.init === 'function') {
        (window as any).H5P.init();
    }
  11. Configure user state, AJAX, and xAPI reporting

    master

    Use these options to manage learner state and automate data reporting via AJAX:

    State & Data Options:

    • contentUserData: Array of objects to pass previously saved state. Format: [{ dataType: 'state', previousState: 'JSON_STRING', subContentId: '*' }].
    • saveFreq: Number. Autosave interval in seconds. Required if contentUserData or ajax is set. Set to false to disable.
    • postUserStatistics: Boolean. If true, H5P posts results upon a finish event. Requires ajax.setFinishedUrl to be set. Default: false.

    AJAX & User Options:

    • ajax: Object. Required for managing learner state via AJAX.
      • ajax.setFinishedUrl: String. URL to POST results to upon finish (requires postUserStatistics: true).
      • ajax.contentUserDataUrl: String. Endpoint to GET/POST user state (requires user property).
    • user: Object. Current user data.
      • user.name: String. Name of the xAPI actor. Required if user is provided.
      • user.mail: String. Email of the user. Required if user is provided.
  12. Choose between iframe and div embedding modes

    master

    The H5PStandalone player supports two embedding modes via the embedType option:

    1. iframe (Default): Creates an <iframe> inside a wrapper div. This provides better isolation for the H5P content, preventing CSS and JS collisions with your main application. The player loads core assets into the iframe.

    2. div: Renders the H5P content directly into a <div> within your page. This mode injects all required H5P scripts and stylesheets directly into the main document's <head> or the anchor element. Use this if you need the H5P content to interact more closely with the host page's DOM, but be aware of potential style/script conflicts.

    // Using iframe mode (default)
    new H5PStandalone(element, { embedType: 'iframe' });
    
    // Using div mode
    new H5PStandalone(element, { embedType: 'div' });