Only this pageAll pages
Powered by GitBook
1 of 31

Developers

Loading...

Widget

Loading...

Loading...

Loading...

Loading...

dashboard

Loading...

Annoto Player

Loading...

Loading...

Loading...

Kaltura Plugin

Loading...

Loading...

Loading...

Loading...

Kaltura Plugin (V2 Legacy)

Loading...

Loading...

Loading...

Loading...

Loading...

Wistia Plugin

Loading...

Loading...

Loading...

Loading...

Integrations

Loading...

Loading...

Playground

After Running the Pen, hit the "Edit On Codepen" to see the demo.

Playground

After Running the Pen, hit the "Edit On Codepen" to see the demo.

Youtube Example

Vimeo Example

Advanced Workflows

Single Page Applications

Single page applications dynamically add and remove DOM elements on a single webpage. To provide solution for the dynamic nature of the applications, the player has APIs to close the player when elements are removed, and to reload the player into new DOM elements:

player.setup();
player.close();
player.reset();
player.setupWidget();
player.loadWidget();
player.destroyWidget();

While multiple players can be setup on a single page. The Widget can be loaded only to a single player at a time. The widget is managed by a singleton, each time one of the players calls loadWidget(), the widget is loaded in that player.

For more details please refer to the .

Advanced Workflows

Annoto application container

Annoto widget is set as absolute meaning it doesn't influence the website layout. By default the application is contained inside a single DIV that is appended to the BODY.

If you require different behavior, you can put the following DIV anywhere in your DOM:

Once this DIV element is present in the DOM, when Annoto.boot() is called, it would be detected automatically by Annoto and serve as container for the Annoto app.

Getting Started

If you are looking to integrate Annoto as a widget without influencing website layout, as an overlay on top your existing video player, you are in the right place :)

At the moment we support the following players:

  • FlowPlayer

WidgetService
Player API

Customise Annoto Widget configuration

Comming soon

Using Annoto Widget API

Comming soon

H5P

  • HTML5

  • JwPlayer

  • Kaltura V2/V7

  • OpenEdx

  • Plyr

  • VideoJS

  • Vimeo

  • Wistia

  • YouTube

  • Page Embed

  • Custom Player

  • Setup

    Simply add the following script:

    And bootstrap Annoto with the desired Application configuration:

    If you are using a javascript module loader such as RequireJS. Annoto exposes a standard UMD. For example for requireJS:

    Quick Start

    The code snippet below provides a quick start for:

    • Widget setup

    • How to provide media details metadata (such as title, description, custom identifier)

    • How to provide group (course) details (identifier, title, description). This allows to build site hierarchy of courses/channels/etc. Once provided the discussion is scoped to the group, allowing reuse of same video in multiple groups.

    • User SSO (Single Sign On) authentication.

    • Dynamically load/destroy the widget for SPA (Single page applications)

    Configuration

    Annoto application providers flexibility in how it integrates in the website. For full configuration options, please refer to the Config Reference. Annoto API Typescript interfaces is available via the @annoto/widget-api package.

    Using the API

    Annoto application exposes an API. To get access to the API use the "ready" event:

    For full documentation, please refer to the AnnotoApi Reference.

    For more elaborate example of api usage, please refer to the Quick Start.

    Browser Compatibility

    Annoto is compatible with all major evergreen browsers (Edge, Chrome, Mozilla, Safari).

    <script src="https://cdn.annoto.net/widget/latest/bootstrap.js"></script>
    <script>
      // Minimal Application configuration
      var config = {
        /* set to your client id after registration. */
        clientId: '',
        widgets: [
          {
            player: {
              /* type of the player */
              type: 'youtube',
              /* DOM element or DOM query selector of the player */
              element: '#player-element'
            }
          }
        ],
      };
    
      window.Annoto.boot(config);
    </script>
    requirejs(["https://cdn.annoto.net/widget/latest/bootstrap.js"], function(Annoto) {
        //This function is called when annoto-bootstrap.js is loaded.
        Annoto.boot(config);
    });
    import { Annoto, IAnnotoApi, IConfig } from '@annoto/widget-api';
      
    interface Global extends Window {
      Annoto: Annoto;
    }
    const global = window as Global;
    
    const annotoConfig: IConfig = {
      clientId: 'eyJhbGciOiJIUzI1...', // ClientId provided by Annoto
      group: {
        id: '2613a9a5-7245', // custom group/course/channel identifier
        title: 'Annoto Getting Started Course',
        // description: '...', // (optional)
      },
      hooks: {
        mediaDetails: (): IMediaDetails | Promise<IMediaDetails> => {
          return {
            title: 'Quick Start',
            // id: '7245-asfa', // (optional) unique media identifier
            description: 'Quick start for setup on custom platform', // (optional)
          };
        }
      },
      widgets: [
        {
          player: {
            type: 'videojs', // type of the player
            element: '.video-js' // DOM element or DOM query selector of the player
          }
        }
      ],
    };
    
    let annotoAPI: IAnnotoApi; // api object placeholder
    
    /**
     * call to authenticate the user using SSO
     * @param token - JWT user token
     */
    const authAnnoto = async (token: string) => {
      if (annotoApi) {
        return await annotoApi.auth(token);
      }
      console.log('Annoto api or sso token not ready yet');
    }
    
    /**
     * boot Annoto or loaded a new configuration (for example on DOM change)
     */
    const loadOrBootAnnoto = async (config: IConfig) => {
      if (annotoApi) {
        return annotoApi.load(config);
      }
      
      global.Annoto.on('ready', (api: IAnnotoApi) => {
        annotoApi = api;
        authAnnoto();
      });
      return global.Annoto.boot(config);
    }
    
    /**
     * Destroy the widget and remove from DOM
     * Can be used by single page applications when leaving the page
     */
    const destroyAnnoto = async () => {
      if (annotoApi) {
          return annotoApi.destroy();
      }
    }
     
    // Somewhere on the page with video where annoto needs to be loaded
    loadOrBootAnnoto(annotoConfig);
    // Application configuration
    const annotoConfig = {
      /* ... */
    };
      
    let annotoAPI;
    window.Annoto.on('ready', function (api) {
      annotoAPI = api;
      // Authenticate SSO User
      annotoApi.auth(userToken);
    });
    
    window.Annoto.boot(config);
    This is especially useful if BODY is not your main scrollable layout.

    As a rule of thumb always place the container in the same scrollable element as your video.

    Custom Widget Fab Button

    When the Annoto widget is closed a small Fab button with comments icon is positioned next to the player to allow users to open the widget.

    In some cases you might want to implement your own open/close UI and functionality.

    To hide the default Fab button set the fabDisable Ux property to true:

    To show/hide the widget, use the IAnnotoApi show() and hide() methods. For more details on how to obtain the api object please refer to Using the API.

    Annoto Timeline

    Annoto Timeline allows users to spot points of interest. The timeline can be embedded in the player or it can be positioned below the player. Lets explore the timeline configuration options:

    By default, the timeline will be positioned under the player:

    If overlay is true, the timeline will be positioned inside the controls of the player:

    LTI Outcomes

    Annoto supports reporting grading to LMS (Learning Management System) using the IMS LTI 1.3 AGS.

    If Annoto is integrated in a 3d party system that is taking the role of LTI Tool that processes the LTI messaging requests, Annoto still supports taking care of managing the outcomes reporting.

    The above setup requires the below configurations.

    Once setup, Annoto backend services will take care of always keeping the grades up to date, including retries (in case of LMS rate limiting).

    LTI Outcomes Webhook Integration

    For Backend Webhook setup please refer to Webhook integration for details.

    LTI Resource Link Request AGS Claim Forwarding

    During LtiResourceLinkRequest the LMS (Platform) provides the following claims:

    Those claims need to be provided to the mediaDetails of the widget:

    integrationKey is provided by Annoto during setup.

    Custom Video Player

    Annoto providers ends users, advanced interaction with video content. In order to do it Annoto requires access to video player API. Before Annoto can perform any action it must obtain the media source and the duration of the media.

    We already support a variety of widely used player types, and constantly adding new ones. If you are using a custom player or player we don't support yet, you have 2 options:

    1. Implement a PlayerAdaptorApi and set it in Widget's player configuration. Contributions are very welcome and appreciated :)

    2. Contact us, we are happy to help.

    Analytics Events

    Annoto widget exposes api to subscribe to analytics events so that can be used in custom integrations, for example ingested and analyzed by custom BI systems.

    To listen to the events use the familiar Annoto.on() api.

    The widget provides two types of events:

    Stats Events

    Discrete events of user actions such as submit a comment, vote, read a reply, click on timestamp, first video play, etc.

    For more details on the event callback please refer to the AnnotoStatsEventCallback interface. For full list of event types, please refer to StatsTypeEnum.

    Video Benchmark Events

    Video analytics events that provides details about the current and historical accumulated video consumption of the user.

    For more details on the event callback please refer to the AnnotoVideoBenchmarkCallback definition.

    Interactions Stats Events

    Interactions events of user actions that provide details about interactions consumed and the results of the activity.

    For more details on the event callback please refer to the AnnotoCtaStatsEventCallback definition.

    position
    const annotoConfig: IConfig = {
      hooks: {
        mediaDetails: () => {
          return {
            outcomes: {
              clientId: "7f84098a3f85",
              integrationKey: 'U0N7wvTXdXTIx3A204tWg',
              lti: {
                  userId: 'abc123',
                  resourceLinkId: 'xyz321',
                  claim: {
                      lineitems: 'https://example.lms.site/48c52e65-5fb9-4099-9b2e',
                      scope: [
                          'https://purl.imsglobal.org/spec/lti-ags/scope/lineitem',
                          'https://purl.imsglobal.org/spec/lti-ags/scope/lineitem.readonly',
                          'https://purl.imsglobal.org/spec/lti-ags/scope/score',
                          'https://purl.imsglobal.org/spec/lti-ags/scope/result.readonly',
                      ],
                  },
              },
            },
            title: 'Quick Start',
            // id: '7245-asfa', // (optional) unique media identifier
            // description: 'Quick start for setup on custom platform', // (optional)
          };
        }
      },
      // rest of the widget configuration
    };
    export interface IOutcomes {
      /**
       * integration key provided by Annoto during setup
       * required if lti object is provided
       */
      integrationKey?: string;
      /**
       * LTI outcomes parameters provided by the platform during launch
       */
      lti?: ILtiAGSOutcome;
      /**
       * if set to true, outcome passback support is expected for the activity, and if not
       * present via this config (or other means), a warning to user will be shown
       * notifying of potential issue with passing back grades to 3d party platform (such as LMS)
       * @default true
       */
      isExpected?: boolean;
    }
    
    export interface ILtiAGSOutcome {
      /**
       * Tool deployment client_id
       * If provided will be forwarded in the Webhook outcome messages
       */
      clientId?: string;
      /**
       * The userId MUST be present and MUST be the same as the resource link LTI parameter 'user_id'.
       */
      userId: string;
      /**
       * id of the https://purl.imsglobal.org/spec/lti/claim/resource_link claim
       */
      resourceLinkId: string;
      /**
      * This claim is included in LTI messages if any of the
      * Assignment and Grade Services are accessible by the tool in the context of the LTI message.
      * https://www.imsglobal.org/spec/lti-ags/v2p0#assignment-and-grade-service-claim
      */
      claim: {
        /**
        * the endpoint URL for accessing the line item container for the current context.
        */
        lineitems: string;
        /**
         * when an LTI message is launching a resource associated to one and only one lineitem,
         * the claim must include the endpoint URL for accessing the associated line item;
         * in all other cases, this property must be either blank or not included in the claim.
         */
        lineitem?: string;
        /**
         * An array of scopes the tool may ask an access token for.
         */
        scope?: LtiAGSScopeType[];
      }
    }
    <div id="annoto-app"></div>
    var config = {
        ux: {
          widget: {
            fabDisable: true
          }
        }
        // ...
      };
    
    timeline: {
        overlay: true
    }
    https://purl.imsglobal.org/spec/lti-ags/claim/endpoint
    https://purl.imsglobal.org/spec/lti/claim/resource_link
    Annoto.on('stats_events', function (ev) {
        console.log(ev);
    });
    Annoto.on('video_benchmark', function (ev) {
        console.log(ev);
    });
    Annoto.on('cta_stats_events', function (ev) {
        console.log(ev);
    });

    Using Annoto Widget API

    In Customise Annoto Widget configuration we have learned how to listen to Kaltura player events and get access to the widget configuration and the API:

    kWidget.addReadyCallback(function (playerId) {
            player.kBind('annotoPluginReady', function (annotoApi) {
                    // Make use of the Annoto API
            });
    });

    The annotoApi can be used to accomplish various tasks such as dynamically hide/show the widget, load new configuration and maybe the most common task to perform Single Sign On (SSO) authentication:

    var userJwtToken = '....';
    player.kBind('annotoPluginReady', function (annotoApi) {
        // Make use of the Annoto API
        // If we already have the userJwtToken use it,
        // alternatively the annotoAPI can be saved in a variable
        // and used asynchronously.
        annotoApi.auth(userJwtToken).catch(function(err) {
            console.error('Annoto SSO auth error: ', err);
        });
    });

    For more information regarding SSO, please refer to the Annoto SSO Technical Guide or contact us at [email protected].

    Customise Widget Configuration

    The Annoto Widget is loaded inside the wistia player iframe so direct access to the Widget API and configuration is not possible.

    Luckily we provide a helper script that exposes API to configure the widget and perform API actions.

    Under the hood, we use postMessage for communicating with the widget in the Iframe.

    Load the Iframe API Script

    Setup the Iframe Client API

    The Iframe Client API requires the Wistia player Iframe embed element as constructor argument: new AnnotoIframeApi.Client(document.getElementById("wistia-player-id"))

    annoto.onSetup() provides opportunity to config set the , for example configure the clientId (Annoto API key).

    Once configuration object is ready, call next(config) to bootstrap the widget inside the Wistia player iframe.

    config.widgets[0].player.type and element are setup for Wistia internally by the plugin.

    If player configuration callbacks are provided: config.widgets[0].player.mediaDetails() config.widget[0].player.mediaSrc() They are called synchronously when next(config) is called. So the media details and the source should be already available at the time when next(config) is called. The callbacks are called once during the setup. It is NOT recommended to use theconfig.widget[0].player.mediaSrc()for the Wistia player plugin.

    Getting Started

    Annoto plugin integration for the Kaltura player

    If you are using Kaltura Media Space or KAF (Kaltura integration inside LMSs), Annoto is a premier Kaltura partner and can be enabled in your platform with a click of a button.

    If you are using custom Kaltura player embed on your platform you are in the right place!

    Kaltura player allows extending its functionality via an extensive plugin system. To simplify Annoto integration within the Kaltura player, we have developed a plugin that allows adding Annoto with a simple script while allowing full customisation of the Annoto Widget.

    For more information on Kaltura player embed please refer to .

    For more information on the Kaltura player toolkit and plugins please refer to .

    The integration of Annoto plugin consist of 2 simple steps:

    1. Setup the Annoto plugin using a script tag.

    2. (Optional) Implement Single Sign On (SSO) integration, modify the Widget configuration and use the API.

    Using Widget API

    In we've setup the Widget, similar to onSetup hook the Iframe Client API provides onReady hook. Once onReady is called, Annoto API actions can be performed:

    The api can be used to accomplish various tasks such as dynamically hide/show the widget, load new configuration and maybe the most common task to perform Single Sign On (SSO) authentication as demonstrated above.

    For full API documentation, please refer to the

    Setup using Universal Studio (KMC)

    Please follow the steps below to setup the plugin:

    • Login to KMC at .

    • At the "Studio" choose your player.

    • Navigate to the plugins menu on the left panel:

    Setup using Javascript (Optional)

    Advanced topic

    Please refer to for the recommended setup method.

    If setting up Annoto plugin via Kaltura universal studio is not possible, or simply if more customisation is required. It is possible to compile a custom Annoto plugin for the Kaltura player and add it to the player via Javascript.

    Getting Started

    The plugin method is only for the , for regular embeds, please use the (set player type to wistia).

    Wistia player allows extending its functionality via an extensive . To enable Annoto integration within the we have developed a plugin that allows adding Annoto via simple configuration.

    Setup Annoto Plugin

    Wistia iframe embeds take the exact same JSON parameters as a Standard embed, but they must be properly URL-encoded using a bracket syntax.

    To add the Annoto plugin, add the following URL query param to the iframe src:

    For example an iframe embed may look something like that:

    For more information please refer to .

    For more information on the Wistia player and plugins please refer to Wistia documentation.

    The integration of Annoto plugin consist of 3 simple steps:

    1. Setup Annoto plugin in the Wistia player embed code.

    2. Add javascript to modify the configuration of the Widget to fit your needs.

    3. (Optional) Implement Single Sign On (SSO) integration and use the widget API.

    Wistia player Iframe embed
    standard widget integration method
    plugin system
    Wistia player Iframe embed
    Widget Configuration
    .

    The api object provided by the Iframe Client annoto.onReady has limited capabilities:

    1. It does not support Promise based usage. It supports only callbacks.

    2. It support only the following methods: auth, load, close, show, hide, registerOriginProvider, logout.

    3. registerOriginProvider supports only getPageUrl.

    For more information regarding SSO, please refer to the Annoto SSO Technical Guide or contact us at [email protected].

    Customise Widget Configuration
    AnnotoApi Reference
    The widget would not load in the Wistia player Iframe yet. The widget is bootstrapped and loaded once the setup is complete. Please read Customise Widget Configuration for more details.
    plugin%5Bannoto%5D%5Bsrc%5D=https%3A%2F%2Fcdn.annoto.net%2Fwistia-plugin%2Flatest%2Fplugin.js
    <iframe src="https://fast.wistia.net/embed/iframe/r440odi4pc?seo=false&videoFoam=true&&plugin%5Bannoto%5D%5Bsrc%5D=https%3A%2F%2Fcdn.annoto.net%2Fwistia-plugin%2Flatest%2Fplugin.js" title="Borrowed video: Welcome to Wistia!" allow="autoplay; fullscreen" allowtransparency="true" frameborder="0" scrolling="no" class="wistia_embed" name="wistia_embed" allowfullscreen msallowfullscreen width="100%" height="100%"></iframe>
    Wistia docs

    Please notice, according to Kaltura documentation:

    "Flashvar based resource includes will only work on relative paths. You should save absolute external resource URLs to your configuration file for them to work in your production players."

    Annoto plugin is an open source and can be compiled into a single javascript file: https://github.com/Annoto/kaltura-plugin

    Setup using Universal Studio (KMC)
    kWidget.featureConfig({
    	'flashvars': {
    		"annoto": {
        	//This line turn on the plugin
          'plugin': true,
          //This line sets the plugin file path
          "iframeHTML5Js" : "./plugin.js",
        },
    	},
    	// The rest of the Kaltura configuration
    	// The below is just an example
    	'targetId' : 'kaltura_player',
    	'wid' : '_243342',
    	'uiconf_id' : '5994862',
    	'entry_id' : '0_uka1msg4',
    })
    <iframe src="https://fast.wistia.net/embed/iframe/r440odi4pc?seo=false&videoFoam=true&&plugin%5Bannoto%5D%5Bsrc%5D=https%3A%2F%2Fcdn.annoto.net%2Fwistia-plugin%2Flatest%2Fplugin.js" title="Borrowed video: Welcome to Wistia!" allow="autoplay; fullscreen" allowtransparency="true" frameborder="0" scrolling="no" class="wistia_embed" name="wistia_embed" allowfullscreen msallowfullscreen width="100%" height="100%"></iframe>
    <script src="https://cdn.annoto.net/widget-iframe-api/latest/client.js"></script>
    <iframe src="https://fast.wistia.net/embed/iframe/r440odi4pc?seo=false&videoFoam=true&&plugin%5Bannoto%5D%5Bsrc%5D=https%3A%2F%2Fcdn.annoto.net%2Fwistia-plugin%2Flatest%2Fplugin.js" title="Borrowed video: Welcome to Wistia!" allow="autoplay; fullscreen" allowtransparency="true" frameborder="0" scrolling="no" class="wistia_embed" name="wistia_embed" allowfullscreen msallowfullscreen width="100%" height="100%"></iframe>
    <script src="https://cdn.annoto.net/widget-iframe-api/latest/client.js"></script>
    <script>
        var annoto = new AnnotoIframeApi.Client(document.getElementById("wistia-player-id"));
        annoto.onSetup(function(next) {
            next({
                clientId: 'eyJhbGciOiJIUzI1NiJ9...',
            });
        });
    </script>
    <iframe src="https://fast.wistia.net/embed/iframe/r440odi4pc?seo=false&videoFoam=true&&plugin%5Bannoto%5D%5Bsrc%5D=https%3A%2F%2Fcdn.annoto.net%2Fwistia-plugin%2Flatest%2Fplugin.js" title="Borrowed video: Welcome to Wistia!" allow="autoplay; fullscreen" allowtransparency="true" frameborder="0" scrolling="no" class="wistia_embed" name="wistia_embed" allowfullscreen msallowfullscreen width="100%" height="100%"></iframe>
    <script src="https://cdn.annoto.net/widget-iframe-api/latest/client.js"></script>
    <script>
        var annoto = new AnnotoIframeApi.Client(document.getElementById("wistia-player-id"));
        annoto.onSetup(function(next) {
            next({
                clientId: 'eyJhbGciOiJIUzI1NiJ9...',
            });
        });
        annoto.onReady(function(api) {
            // recomended, so notifications will have URL to valid pages
            api.registerOriginProvider({
                getPageUrl: function() {
                    return location.href;
                },
            });
            var token = 'eyJhbGciO...';
            api.auth(token, function(err) {
                if (err) {
                    console.error('sso auth error: ', err);
                }
            });
        });
    </script>
    • Create a new plugin by pressing on the ‘Create New Plugin’ button and name it annoto.

    Naming the plugin "annoto" is mandatory (this is just how Kaltura works).

    • Define the following Configuration options:

      • iframeHTML5Js : https://cdn.annoto.net/kaltura-plugin/latest/plugin.js

      • customerKey : eyJhbGciOiJ...

        • The customer key is provided by Annoto.

    Specifying the customer key is optional and can be done at Customise Annoto Widget configuration step.

    If Annoto LTI is used, the customerKey is not required

    cid:8cdba515-1bfc-4ad9-bca6-95bdbcc9f81a

    If customerKey is not provided, Annoto Will load in Demo Mode.

    Demo Mode allows you to try out the product but all comments created are not saved after page refresh.

    • Save the player settings.

    https://kmc.kaltura.com
    Kaltura documentation

    Getting Started

    If you are looking for industry leading Video Player with dual synchronous video playback, advanced layout capabilities and the power of Annoto interactivity, you are in the right place :)

    Setup

    The player is officially available via CDN and npm. Annoto works out of the box with not only HTML <script> tag, but also with all major bundlers/packagers/builders, such as Browserify, WebPack, etc.

    The package includes full typings and can be used in typescript/ES6/flow projects.

    Creating a Player

    Annoto player is exposed as , meaning it offers compatibility with the most popular script loaders and can be imported as commonjs/amd/es6. If you are not using any standard loader the player will be exposed on window as global variables.

    If you are using a javascript module loader such as RequireJS. Annoto exposes a standard . For example for requireJS:

    The main player class is . After you have the player instance it can be used to load video sources, dynamically control Widget, get access to Widget API and much more.

    Creating player using the annotoPlayer helper (CDN)

    The annotoPlayer() helper is available only for the CDN. When using the npm package, please use the AnnotoPlayer constructor directly (see below)

    The simplest way to create a player is using the :

    Creating player using the AnnotoPlayer constructor

    The Player instance can be created using the constructor which allows the most flexibility:

    Player Options

    Annoto Player extends standard with some advanced functionality, for more details, please refer to the .

    PlayerOptions extends VideoJS options, so all options supported by the videojs player can be used.

    Using the Widget API

    The most common widget related api is available directly via the instance, for example: setupWidget, loadWidget, destroyWidget.

    For more advanced usages the can be obtained using await player.getWidgetApi()

    Authenticating the User

    The most recomended wait is to provide the User's SSO token to the widget configuration as show in .

    An alternative way is to authenticate the user dynamically

    Getting Started

    Annoto plugin integration for the Kaltura player

    If you are using Kaltura Media Space or KAF (Kaltura integration inside LMSs), Annoto is a premier Kaltura partner and can be enabled in your platform with a click of a button.

    If you are using custom Kaltura player embed on your platform you are in the right place!

    Kaltura player allows extending its functionality via an extensive plugin system. To simplify Annoto integration within the Kaltura player, we have developed a plugin that allows adding Annoto with a simple flashvars configuration while allowing full customisation of the Annoto Widget.

    For more information on the Kaltura player toolkit and plugins please refer to .

    The integration of Annoto plugin consist of 3 simple steps:

    1. Setup Annoto plugin in the Kaltura universal studio (KMC) or using custom javascript.

    2. Add javascript to modify the configuration of the Widget to fit your needs.

    3. (Optional) Implement Single Sign On (SSO) integration and use the widget API.

    Kaltura documentation
    <body>
        <script src="https://player.annoto.net/index.js"></script>
    </body>
    <script>
        // Annoto modules is available at 'nn' global object:
        // nn.annotoPlayer
    </script>
    UMD module
    UMD
    AnnotoPlayer
    annotoPlayer() method
    AnnotoPlayer
    VideoJS options
    PlayerOptions
    AnnotoPlayer
    IAnnotoApi
    Creating A Player
    Using the Widget API
    $ npm install @annoto/player
    requirejs(["https://player.annoto.net/index.js"], function(nn) {
        //
    });
    import { annotoPlayer, IPlayerOptions } from '@annoto/player';
    import { IConfig } from '@annoto/widget-api';
    
    const options: IPlayerOptions;
    const wConfig: Omit<IConfig, 'widgets'> = {
        clientId: '...', // [mandatory for widget] the clientId provided by Annoto
        ssoToken: '...', // JWT user SSO token (recomended, but can be dynamicaly authenticated using the API as well)
    };
    let player: AnnotoPlayer;
    
    annotoPlayer(document.querySelector('annoto-player'), options).then((p) => {
        player = p;
        return Promise.all([
            player.loadSrc({
                src: 'https://www.youtube.com/watch?v=1T9EZi7KJcc',
                type: 'video/youtube'
            }),
            player.setupWidget(wConfig),
        ]);
    }).then(() => {
        return player.loadWidget(); // until this api call, the widget is not shown
    }).catch((err) => {
        console.error('player setup error: ', err);
    });
    <body>
        <annoto-player></annoto-player>
    </body>
    import {
        AnnotoPlayer,
        IPlayerOptions,
        WidgetService,
        IWidgetService,
        getConsoleLogger,
    } from '@annoto/player';
    import { IConfig } from '@annoto/widget-api';
    
    const options: IPlayerOptions; // optional player options
    const wConfig: Omit<IConfig, 'widgets'> = {
        clientId: '...', // [mandatory for widget] the clientId provided by Annoto
        ssoToken: '...', // JWT user SSO token (recomended, but can be dynamicaly authenticated using the API as well)
    };
    // IMPORTANT: there must be only a single WidgetService on a page
    // same widgetService must be used for all the players on a page
    const widgetService = new WidgetService(); 
    
    async function setupPlayer(el: HTMLElement, opt?: IPlayerOptions): Promise<AnnotoPlayer> {
        const pl = new AnnotoPlayer(
            getConsoleLogger('AnnotoPlayer'),
            widgetService,
        );
        
        await pl.setup(el, opt);
        return pl;
    }
    
    let player: AnnotoPlayer;
    setupPlayer(document.querySelector('annoto-player'), options).then((p) => {
        player = p;
        return Promise.all([
            player.loadSrc({
                src: 'https://www.youtube.com/watch?v=1T9EZi7KJcc',
                type: 'video/youtube'
            }),
            player.setupWidget(wConfig),
        ]);
    }).then(() => {
        return player.loadWidget(); // until this api call, the widget is not shown
    }).catch((err) => {
        console.error('player setup error: ', err);
    });
    <body>
        <annoto-player></annoto-player>
    </body>
    const api = await player.getWidgetApi();
    const ssoToken = '...';
    api.auth(ssoToken);

    Customise Annoto Widget configuration

    When the Annoto plugin is loaded by the Kaltura player, it triggers custom events that allows you to get access to the configuration and the API for full customisation and control of the widget.

    Listen to Kaltura Player events

    To subscribe to Kaltura player events please use Kaltura player API:

    For detailed information on the Kaltura player API, please refer to

    Modify Annoto configuration

    annotoPluginSetup is triggered first by the Annoto plugin and provides opportunity to modify the Annoto widget configuration before the .

    Once the widget is bootstrapped by the plugin annotoPluginReady is triggered and provides access to the .

    Below is an example of modifying some basic widget options:

    config is already setup by the Kaltura Annoto plugin, so we need only to override the required configuration, such as clientId, getPageUrl hook, etc. DO NOT CHANGE THE PLAYER TYPE OR PLAYER ELEMENT CONFIG.

    For the default widget configuration used by the plugin please refer to:

    Single Sign On

    The plugin exposes annotoPluginReady event that provides access to the for implementing the functionality.

    Asynchronous Widget Bootstrap

    Advanced topic

    In some cases you might not have the required configuration for the Annoto Widget when annotoPluginSetup is triggered, or would want to postpone the bootstrap of the widget until some condition is met.

    Until delayDoneCallback is called, the widget won't be bootstrapped and annotoPluginReady won't be triggered.

    If doneCb is not called, the widget is not bootstrapped. This can be used to dynamically decide on which videos to load the annoto plugin.

    Theming

    Many aspects of the widget UI can be modified via .

    Overriding the Defaults

    To override the defaults simply use .annoto CSS class selector. For Example:

    kWidget.addReadyCallback(function (playerId) {
            var player = document.getElementById(playerId);
            player.kBind('annotoPluginSetup', function (params) {
                    var config = params.config;
                    // Modify the Annoto configuration
            });
            player.kBind('annotoPluginReady', function (annotoApi) {
                    // Make use of the Annoto API
            });
    });
    http://player.kaltura.com/docs/api
    widget is bootstrapped
    Annoto API
    https://github.com/Annoto/kaltura-plugin
    API
    SSO
    player.kBind('annotoPluginSetup', function (params) {
        var config = params.config;
    
        config.clientId = 'eyJhbGciOiJ...';
        config.locale = 'en';
    
        config.hooks.getPageUrl = function() {
            return window.location.href;;
        };
        
        // https://annoto.github.io/widget-api/interfaces/config_hooks.IHooks.html#mediaDetails
        config.hooks.mediaDetails = function(params) {
            return {
                ...params?.details,
                // Optionaly use your own video identifier
                // https://annoto.github.io/widget-api/interfaces/config_media_details.IMediaDetails.html#id
                // id: 'unique_video_identifier',
            };
        };
        
        config.hooks.ssoAuthRequestHandle = function() {
            // trigger user login
            // https://annoto.github.io/widget-api/interfaces/config_hooks.IHooks.html#ssoAuthRequestHandle
            window.location.replace('https://example.com/login');
        };
    });
    player.kBind('annotoPluginReady', function (annotoApi) {
        // token is the generated user authentication JWT token
        annotoApi.auth(token);
    });
    var delayDoneCallback;
    var annotoConfig;
    player.kBind('annotoPluginSetup', function (params) {
        
        // Set params.await to the following function:
        params.await = function (doneCb) {
            delayDoneCallback = doneCb;
        };
        annotoConfig = params.config;
    });
    
    // do some async work and when ready call the delayDoneCallback:
    
    setTimeout(function() {
        // Modify the Annoto configuration asynchronously
        annotoConfig.clientId = 'eyJhbGciOiJ...';
        delayDoneCallback();
    }, 200);
    Widget Utility Classes

    The widget has different utility classes that are appended to the .annoto class based on a situation. The styling can be modified using combinations of those classes. For example, the most common state is dark theme of the widget:

    For dark theme styling but only when the player is in full screen:

    Below is a list of the most useful utility classes:

    Utility Class

    Description

    nnc-dark-theme

    Used it different cases such as when widget is an overlay inside the player, player is in full screen, or configured as the default theme in the widget configuration.

    nn-player-fs

    Added when player is in full screen

    nnc-mobile

    Added when the user uses a mobile device, and the widget applies UX for mobile devices.

    nn-is-open

    Widget is in open state.

    nn-[PAGE]-page

    Changed based on the view the widget is showing. Common views:

    nn-notes-page nn-comments-page nn-preferences-page

    Please notice in most cases nnc prefix is used. but in some cases nn is used instead.

    Colors Palette

    Main Colors

    The main colors used by the widget are: primary, secondary, tertiary, light, medium, dark, darkest, success, warning, notice, danger.

    Each main color has variants for different effects: contrast, shade and tint

    Some or all the colors can be modified.

    If for example primary color is modified, please make sure to set all it's variants to appropriate values, a great helper tool by Ionic can be used to generate the variants: https://ionicframework.com/docs/theming/color-generator

    Base Colors

    Utility Colors

    Vendor Colors

    Those are standard colors of third party, usually they do not need to be changed.

    Step Colors

    Those colors are different shades of grey used by the widget. Please notice, the main colors light, medium, dark and darkest are used for styling.

    In most cases there is no need to change any of the steps.

    Typography

    Font Family

    Fonts

    Components

    Timeline

    Comments

    Notes

    Chat

    Interactions

    Tooltips

    Scrollbar

    Misc

    CSS Variables

    Setup the Plugin

    Setup of the Annoto plugin requires the following conditions to be met:

    1. Kaltura player must be embedded using any form of embed except the IFrame embed.

    2. Annoto plugin script loaded on page after the Kaltura Player script.

    3. Annoto plugin configuration added to the Kaltura player.

    There are 2 supported approaches to meet the conditions, each has it own cons/pros:

    .annoto {
        --nnc-color-dark: #333333;
    }
    .annoto.nnc-dark-theme {
        --nnc-color-dark: #cecece;
    }
    .annoto.nnc-dark-theme.nn-player-fs {
        --nnc-color-dark: #b2b2b2;
    }
    .annoto {
        --nnc-color-primary: #ea5451;
        --nnc-color-primary-contrast: #ffffff;
        --nnc-color-primary-shade: #ce4a47;
        --nnc-color-primary-tint: #ec6562;
        --nnc-color-primary-rgb: 234,84,81;
        --nnc-color-primary-contrast-rgb: 255,255,255;
        
        --nnc-color-secondary: #00a9a4;
        --nnc-color-secondary-contrast: #ffffff;
        --nnc-color-secondary-shade: #009590;
        --nnc-color-secondary-tint: #1ab2ad;
        --nnc-color-secondary-rgb: 0,169,164;
        --nnc-color-secondary-contrast-rgb: 255,255,255;
        
        --nnc-color-tertiary: #5450e8;
        --nnc-color-tertiary-contrast: #ffffff;
        --nnc-color-tertiary-shade: #4a46cc;
        --nnc-color-tertiary-tint: #6562ea;
        --nnc-color-tertiary-rgb: 84,80,232;
        --nnc-color-tertiary-contrast-rgb: 255,255,255;
        
        --nnc-color-light: #f4f5f8;
        --nnc-color-light-contrast: #000000;
        --nnc-color-light-shade: #d7d8da;
        --nnc-color-light-tint: #f5f6f9;
        --nnc-color-light-rgb: 244,245,248;
        --nnc-color-light-contrast-rgb: 0,0,0;
        
        --nnc-color-dark: #35353a;
        --nnc-color-dark-contrast: #ffffff;
        --nnc-color-dark-shade: #2f2f33;
        --nnc-color-dark-tint: #49494e;
        --nnc-color-dark-rgb: 53,53,58;
        --nnc-color-dark-contrast-rgb: 255,255,255;
        
        
        /** same format is used for the other colors
         * medium, darkest, success, warning, notice, and danger
         */
    }
    .annoto {
        --nnc-bg: #ffffff;
        --nnc-bg-color: #ffffff;
        --nnc-color: #000000;
        --nnc-item-bg: #fafafa;
        --nnc-border-color: #e2e2e5;
        --nnc-color-text: rgba(0, 0, 0, .87);
        --nn-widget-bg: rgb(255, 255, 255);
        --nn-widget-bg-secondary: rgb(247, 247, 247);
    }
    .annoto {
        --nnc-color-text-disabled: rgba(0, 0, 0, .26);
        --nnc-focused-box-shadow: 0 0 0 2px rgba(37, 137, 218, .9);
        --nnc-bg-hover: var(--nnc-color-step-950, #eeeeee);
    }
    .annoto {
        --nnc-color-docx: #2B579A;
        --nnc-color-xlsx: #217346;
        --nnc-color-pptx: #B7472A;
    }
    .annoto {
        --nnc-color-step-950: #eeeeee;
        --nnc-color-step-900: #e1e1e1;
        --nnc-color-step-850: #d5d5d5;
        --nnc-color-step-800: #c8c8c8;
        --nnc-color-step-750: #bcbcbc;
        --nnc-color-step-700: #afafaf;
        --nnc-color-step-650: #a3a3a3;
        --nnc-color-step-600: #969696;
        --nnc-color-step-550: #8a8a8a;
        --nnc-color-step-500: #7d7d7d;
        --nnc-color-step-550: #717171;
        --nnc-color-step-400: #646464;
        --nnc-color-step-450: #585858;
        --nnc-color-step-300: #4b4b4b;
        --nnc-color-step-250: #3f3f3f;
        --nnc-color-step-200: #323232;
        --nnc-color-step-150: #262626;
        --nnc-color-step-100: #191919;
        --nnc-color-step-50: #0d0d0d;
        --nnc-color-step-950-rgb: 238, 238, 238;
        --nnc-color-step-900-rgb: 225, 225, 225;
        --nnc-color-step-850-rgb: 213, 213, 213;
        --nnc-color-step-800-rgb: 200, 200, 200;
        --nnc-color-step-750-rgb: 188, 188, 188;
        --nnc-color-step-700-rgb: 175, 175, 175;
        --nnc-color-step-650-rgb: 163, 163, 163;
        --nnc-color-step-600-rgb: 150, 150, 150;
        --nnc-color-step-550-rgb: 138, 138, 138;
        --nnc-color-step-500-rgb: 125, 125, 125;
        --nnc-color-step-450-rgb: 113, 113, 113;
        --nnc-color-step-400-rgb: 100, 100, 100;
        --nnc-color-step-350-rgb: 88, 88, 88;
        --nnc-color-step-300-rgb: 75, 75, 75;
        --nnc-color-step-250-rgb: 63, 63, 63;
        --nnc-color-step-200-rgb: 50, 50, 50;
        --nnc-color-step-150-rgb: 38, 38, 38;
        --nnc-color-step-100-rgb: 25, 25, 25;
        --nnc-color-step-50-rgb: 13, 13, 13;
    }
    .annoto {
        --nnc-font-family: 'Roboto','Helvetica','Arial',sans-serif;
    }
    .annoto {
        --nnc-typo-body-size: 1em;
        --nnc-typo-body-weight: 400;
        
        --nnc-typo-title-size: 1.125em;
        --nnc-typo-title-weight: 400;
        
        --nnc-typo-subhead-size: 1em;
        --nnc-typo-subhead-weight: 500;
        
        --nnc-typo-caption-size: 0.75em;
        --nnc-typo-caption-weight: 400;
    }
    .annoto {
        --nnc-timeline-cta-size: 36px;
    }
    .annoto {  
        --nnc-comment-bubble-bg: rgba(90, 101, 114, 0.06);
        --nnc-comment-font-size: 15px;
        --nnc-comment-avatar-size: 2.25em;
    }
    .annoto {
        --nnc-note-font-size: 15px;
    }
    .annoto {
        --nnc-message-font-size: 15px;
        --nnc-message-bubble-bg: rgba(90, 101, 114, .06);
    }
    .annoto {
        --nnc-cta-item-bg: rgba(90, 101, 114, .06);
    }
    .annoto {
        --nnc-tooltip-color: #fff;
        --nnc-tooltip-bg: rgba(66,66,66, 0.9);
    }
    .annoto {
        --nnc-scrollbar-width: 6px;
        --nnc-scrollbar-thumb-color: var(--nnc-color-step-750, #bcbcbc);
        --nnc-scrollbar-thumb-hover-color: var(--nnc-color-step-600, #969696);
        --nnc-scrollbar-bg: var(--nnc-color-step-850, #d5d5d5);
    }
    .annoto {
        --nnc-card-bg: #ffffff;
        --nnc-form-layout-bg: #ffffff;
        --nnc-item-bg: #ffffff;
    }

    nnc-rtl

    Widget has Right to Left layout.

  • Setup using script tag with Auto Boot - most flexible approach, but requires once to set the configuration parameters in plugin embed code, and can be copy past after.

  • Setup using script tag and KMC - set the configuration parameters in the KMC Player studio advanced settings, but support for embed types is limited and requires careful placement of the Annoto plugin script tag.

  • Both aproaches are explained in detail below.

    The Annoto plugin is provided via our CDN at:

    https://cdn.annoto.net/playkit-plugin/latest/plugin.js

    For resolving issues or testing (not production) in some cases the staging version might be beneficial at:

    https://cdn.annoto.net/staging/playkit-plugin/latest/plugin.js

    Setup using script tag with Auto Boot

    This approach requires only adding the annoto plugin script tag on page and making sure it executes after the Kaltura Player script.

    This is the most flexible approach and supports any form of embed type except IFrame

    Making sure that Annoto plugin script is executed after the Kaltura Player script can be simply achieved by adding the defer attribute to the plugin script tag as demostrated in the examples below. Once added, the Annoto plugin script tag can be placed anywhere on page regardless of Kaltura Player embed.

    The annoto plugin configuration is provided via script query parameters or global variables.

    In this approach the plugin picks up the parameters and automatically injects annoto plugin configuration into the Kaltura Player.

    Script Tag query parameters

    Below is the minimal required parameters:

    1. auto_boot=1

    2. api_key=eyJhbGciOiJIUzI1NiJ9...

    For example:

    Notice the defer attribute. It's optional, but if provided, will make sure the browser executes the plugin's script only after page content (including Kaltura Player) is loaded on page.

    Full list of query paramaters

    Query parameter
    Required
    Supported value
    Description

    auto_boot

    '1', 'true'

    informs the plugin to perform automatic setup and load the widget

    api_key

    JWT token string

    provide the api key provided by Annoto

    region

    eu , us , staging

    Global variables

    In case the script tag parameters are hard to read or for some reason not work for your use case, an alternative approach is to use global variables as in the example below:

    Setup using script tag and KMC

    This alternative approach allows to provide the annoto plugin configurations using the Kaltura KMC Player studio advanced settings.

    Notice that (unlike the Legacy V2 Kaltura player) adding the Annoto plugin script tag to the page is strill required.

    In this approach the Annoto plugin script MUST come right after the Kaltura player cdn script tag (see the examples below) and MUST NOT contain the defer attribute. Automatic Embed is not supported using this approach, please seet Setup using script tag with Auto Boot

    Dynamic Player Embed

    Notice the placement of the Annoto plugin script tag must be between the src="https://cdnapisec.kaltura.com/... script tag and the second kaltura player embed script tag and calls for KalturaPlayer.setup()

    Thumbnail Embed

    Notice the placement of the Annoto plugin script tag must be anywere after src="https://cdnapisec.kaltura.com/... script tag, so can be at the end after the embed code copy past.

    Automatic Player Embed

    The Automatic Embed is not supported via this approach please use Setup using script tag with Auto Boot

    KMC Player advanced settings

    Please notice this is required only for this approach and SHOULD NOT be defined for the Setup using script tag with Auto Boot approach.

    Please refer to Kaltura documentation for guide on how to use the Player studio advanced settings.

    The Annoto plugin configuration must be added to the plugins section of the player configuration object:

    Notice the manualBoot: false and the clientId: 'eyJh...' which is in fact the api key mentioned above (just named differently).

    In fact the configuration object is passed directly to the Annoto widget as is, so all Annoto widget configuration options are supported.

    To provide all the mentioned options for the Auto Boot approach Please see the snippet below:

    <script defer src="https://cdn.annoto.net/playkit-plugin/latest/plugin.js?auto_boot=1&api_key=eyJhbGciOiJIUzI1NiJ9.ZjU4MTMyMzgtZTBiNS00NTM2LWE1ZTEtNTliMGZhMjYzZjVm.3vX2MeaEtjKS83Iqj9K_D-3-NWMXAJUoslpQEuTFVGk"></script>
    <script defer src="https://cdn.annoto.net/playkit-plugin/latest/plugin.js"></script>
    <script type="text/javascript">
        window.NN_PLAYKIT_AUTO_BOOT = true;
        window.NN_PLAYKIT_API_KEY = 'eyJhbGciOiJIUzI1NiJ9.ZjU4MTMyMzgtZTBiNS00NTM2LWE1ZTEtNTliMGZhMjYzZjVm.3vX2MeaEtjKS83Iqj9K_D-3-NWMXAJUoslpQEuTFVGk';
        // window.NN_PLAYKIT_REGION = 'us'; // optional
        // window.NN_PLAYKIT_THEME = 'dark'; // optional
    </script>
    <div id="kaltura_player_893609640" style="width: 560px;height: 395px"></div>
    <script type="text/javascript" src="https://cdnapisec.kaltura.com/p/2302901/embedPlaykitJs/uiconf_id/56505062"></script>
    <!-- annoto plugin script must be injected here in the middle of the Kaltura Player embed code -->
    <script src="https://cdn.annoto.net/playkit-plugin/latest/plugin.js"></script>
    
    <script type="text/javascript">
      try {
        var kalturaPlayer = KalturaPlayer.setup({
          targetId: "kaltura_player_893609640",
          provider: {
            partnerId: 2302901,
            uiConfId: 56505062
          }
        });
        kalturaPlayer.loadMedia({entryId: '1_y7gccl5f'});
      } catch (e) {
        console.error(e.message)
      }
    </script>
    <div id="kaltura_player_662715894" style="width: 560px;height: 395px"></div>
    <script type="text/javascript" src="https://cdnapisec.kaltura.com/p/2302901/embedPlaykitJs/uiconf_id/56505062"></script>
    <script src="https://static.kaltura.com/content/static/player-scripts/thumbnail-embed.js"></script>
    <script type="text/javascript">
    try {
      __thumbnailEmbed({
        config:  {
            provider: {
              partnerId: 2302901,
              uiConfId: 56505062
            },
            targetId: "kaltura_player_662715894"
        },
        mediaInfo: {entryId: '1_y7gccl5f'}
      });
    } catch (e) {
      console.error(e.message)
    }
    </script>
    <!-- annoto plugin script can be added after the Kaltura embed code -->
    <script src="https://cdn.annoto.net/playkit-plugin/latest/plugin.js"></script>
    "annoto": {
        "clientId": "eyJh....",
        "manualBoot": false,
        "ux": {
            "theme": "dark"
        },
        "backend": {
            "domain": "us.annoto.net"
        }
    }

    The region of the Annoto service for the API key. The default is eu

    theme

    dark , default

    The prefered widget theme. Please notice that the prefered place to setup is via the Annoto dashboard. This option allows to override for specific embed.

    Webhook

    Introduction

    A webhook is an HTTP request, triggered by an event in a source system and sent to a destination system, often with a payload of data. Webhooks are automated, in other words they are automatically sent out when their event is fired in the source system.

    This provides a way for one system (the source) to "speak" (HTTP request) to another system (the destination) when an event occurs, and share information (request payload) about the event that occurred.

    Annoto backend is the source and will send messages to the destination platform.

    Webhook Security

    Annoto will include a secure signature in all the webhook requests so that the destination platform can verify that the webhook request was generated by Annoto, and not from some server acting like Annoto.

    Since the signature is specific to each and every webhook request, it also helps to validate that the message wasn’t intercepted and modified by someone in between the destination and the source (i.e. Man-in-the-middle attack).

    We will send webhook messages only to valid TLS 1.2 and up (Https) endpoint.

    Security Header

    Each Webhook message will include a signature header:

    X-Annoto-JWS - A JSON Web Signature () signed using the Annoto .

    The X-Annoto-JWS signature is a JSON Web Signature (JWS) and has a detached payload. It is a string that looks like JWS_PROTECTED_HEADER..JWS_SIGNATURE

    JSON Web Signature (JWS) represents content secured with digital signatures or Message Authentication Codes (MACs) using JSON-based [RFC7159] data structures. The JWS cryptographic mechanisms provide integrity protection for an arbitrary sequence of octets.

    The signature generation works as follows:

    • Webhook message is generated (e.g LTI outcomes)

    • The payload is signed using one of the private keys from Anntoo's JSON Web Key Set (e.g. kid: PylK7Us1ntafSdT9RxJ3q

    • The JWS protected header will contain the following standard properties:

    Verifying a Webhook Payload

    To verify that a webhook did actually come from Annoto, you need to compare the JSON Web Signature (JWS) from the X-Annoto-JWS header with the JSON body of the request:

    1. Ensure the X-Annoto-JWS header exists. If it doesn't exist, this request didn't come from Annoto.

    2. Look up the Annoto JWKS at the URL provided by Annoto. This contains the public keys, and should have a kid that matches the kid of the JWS_PROTECTED_HEADER

    3. Use a JWT library for your programming language to verify the body matches the signature.

      To implement the verification, some languages may require you to build URL variant of Base64 Encode of the JWS Payload (the webhook body) in order to verify the JWS.

    as per RFC the kid for an individual JWK is immutable, and therefore it is safe and recommended to cache individual JWK's by their kid indefinitely (a proper JWT library will probably do that for you).

    Example libraries that support RFC-7797 and JWKS, and simplify verifying a JWS:

    • Java:

    • Node.js:

    Pseudo PHP example code

    Webhook Messages

    Webhook messages are POST requests with JSON body:

    LTI Outcomes

    Annoto supports managing the via third party LTI 1.3 Tool. Webhook serving as a proxy medium.

    Webhook messages JSON body:

    kid - The key used to sign the request. This can be looked up in the JWKS

  • alg - Will be RS256

  • The detached JWS will be added as the X-Annoto-JWS header to the Webhook request.

  • The HTTP Request is sent using POST method to the Webhook destination endpoint.

  • The JWS signature uses a detached payload, so it is of the form JWS_PROTECTED_HEADER..JWS_SIGNATURE

    .Net (C#): jose-jwt

    RFC-7797
    JSON Web Key Set (JWKS / RFC-7517)
    com.nimbusds.jose
    jose
    LTI 1.3 Assignment and Grades
    $jws = getRequestHeader('X-Annoto-JWS');
    $encodedPayload = urlsafeB64Encode($rawRequestPayload);
    list($header, $signature) = explode('..', $jws);
    $fullJWT = implode('.', [$header, $encodedPayload, $signature]);
    $publicJwk = getPublicJwkFromAnnotoJwksUrl();
    validateJwt($fullJWT, $publicJwk);
    export interface IWebhookMessagePayload<T extends WebhookType = WebhookTytpe> {
      type: T;
      data: IWebhookDataMap[T];
      /**
       * Unique identifier
       */
      id: string;
      /**
       * Strictly increasing timestamp formatted using ISO 8601 with a sub-second precision.
       */
      timestamp: string;
      /**
       * id of the host
       */
      hostId: string;
    }
    
    export interface IWebhookDataMap {
      ltiOutcome: IWebhookLtiOutcomeData;
    }
    
    export type WebhookType = keyof IWebhookDataMap;
    
    {
      "type": "ltiOutcome",
      "data": {
        "serviceUrl": "https://example.lms.com/lineitem/48c52e65-5fb9-4099",
        "method": "POST",
        "scope": [
          "https://purl.imsglobal.org/spec/lti-ags/scope/lineitem"
        ],
        "version": "1.3",
        "payload": {
          "resourceId": "da498b42-2481-4c5e-8bed-f26e000c9eaf.359af51b-f692-4baf-8ca3-0ae576812ebf",
          "tag": "quiz",
          "scoreMaximum": 1,
          "label": "PEOPLE ARE AWESOME"
        },
        "headers": {
          "Accept": "application/json,application/vnd.ims.lis.v2.lineitem+json",
          "Content-Type": "application/vnd.ims.lis.v2.lineitem+json"
        }
      },
      "id": "q3OqZrUI4Rw7w8cmzr268",
      "timestamp": "2023-01-11T23:59:16.452Z",
      "hostId": "d3a0963f-8b18-4a06-a6c4-c21642e3c24d"
    }
    export interface IWebhookMessagePayload {
      type: 'ltiOutcome';
      data: IWebhookLtiOutcomeData;
      /**
       * Unique identifier
       */
      id: string;
      /**
       * Strictly increasing timestamp formatted using ISO 8601 with a sub-second precision.
       */
      timestamp: string;
    }
    
    /**
     * Webhook for proxy of LTI Outcomes.
     * LTI 1.3/1.1 Service Message Response should be returned as
     * Response to the Webhook API as is, including:
     * Response code
     * Link http header (RFC8288) (For LTI 1.3)
     * Content-Type header
     * Response body
     */
    export interface IWebhookLtiOutcomeData {
      /**
       * LTI version Used
       */
      version: '1.3' | '1.1';
      /**
       * Platform service URL to where to send the Service Message
       */
      serviceUrl: string;
      /**
       * The request method to use for sending the payload to the serviceUrl
       */
      method: RequestMethodType;
      /**
       * Headers to use for the service message
       * such as Content-Type
       */
      headers: Record<string, string>;
      /**
       * Scopes for the Authorization token
       * Required for LTI 1.3.
       */
      scope?: Lti13AGSScopeType[];
      /**
       * JSON Payload (Body) for the Service Message to be forwarded as is
       * can be empty for example for GET lineitem request
       * object for LTI 1.3
       * xml string for LTI 1.1
       */
      payload?: object | string;
      /**
       * LTI 1.3 Tool deployment client_id
       */
      clientId?: string;
    }
    
    export type RequestMethodType = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
    export type Lti13AGSScopeType =
        'https://purl.imsglobal.org/spec/lti-ags/scope/lineitem' |
        'https://purl.imsglobal.org/spec/lti-ags/scope/lineitem.readonly' |
        'https://purl.imsglobal.org/spec/lti-ags/scope/score' |
        'https://purl.imsglobal.org/spec/lti-ags/scope/result.readonly';
    {
      "type": "ltiOutcome",
      "data": {
        "serviceUrl": "https://example.lms.com/lineitem/48c52e65-5fb9-4099",
        "method": "POST",
        "scope": [
          "https://purl.imsglobal.org/spec/lti-ags/scope/lineitem"
        ],
        "version": "1.3",
        "clientId": "7f84098a3f85",
        "payload": {
          "resourceId": "da498b42-2481-4c5e-8bed-f26e000c9eaf.359af51b-f692-4baf-8ca3-0ae576812ebf",
          "tag": "quiz",
          "scoreMaximum": 1,
          "label": "PEOPLE ARE AWESOME"
        },
        "headers": {
          "Accept": "application/json,application/vnd.ims.lis.v2.lineitem+json",
          "Content-Type": "application/vnd.ims.lis.v2.lineitem+json"
        }
      },
      "id": "q3OqZrUI4Rw7w8cmzr268",
      "timestamp": "2023-01-11T23:59:16.452Z",
      "hostId": "ae6bfed6-7f22-4c57-a1d7-e1d6762a0238"
    }
    Kaltura documentation

    SSO (Single Sign On)

    Introduction

    Single Sign-On (SSO) is an authentication mechanism that allows users to access several applications with only one set of login credentials.

    By enabling SSO for your Annoto widget, you become responsible for the authentication of your users: they get authenticated through your own login portal and can use Annoto services freely.

    Process

    1. An unauthenticated user requests access to your site (post login details to your server).

    2. Your server authenticates the user, The user gets authenticated using your own authentication and authorization process.

    3. If the user access is granted, You create a secured JWT payload that contains information about the user, using any standard library.

    4. The JWT token should be part of the login post answer (or some other query as you see fit).

    Annoto will not save the user login session. The annotoAPI.auth(token) should be called at each page load.

    Setup

    What you will get from Annoto:

    • Your clientID

    • A unique secret that will be used to sign JWT tokens.

    THE SECRET MUST BE KEPT CONFIDENTIAL ON YOUR SERVERS.

    JWT Anatomy

    JWT payload should contain the required user information, and be encoded (signed) using the provided SECRET.

    The JWT token MUST be signed using HS256 algorithm.

    The JWT payload should contain:

    If email is not provided, email notifications for users won’t work.

    JWT’s full specification is available at

    JWT Libraries

    There are libraries available for virtually any programming language.

    A good source is:

    PHP Example

  • Your client side JS code should call the annotoAPI.auth(token) method to authenticate the user.

  • unique identifier for the JWT. Equal to the unique identifier of your user.

    Yes

    name

    string

    visible user name

    Yes

    email

    string

    User email account

    No

    photoUrl

    url

    publicly accessible url to user photo

    No

    scope

    string

    scope indicating permissions of the user:

    • ‘user’ - regular user (default)

    • ‘moderator’ - can moderate threads.

    • ‘super-mod’ - can moderate threads and have access to the Annoto dashboard.

    No

    aud

    url

    audience of the token (http://annoto.net)

    No

    Property

    Type

    Description

    Mandatory

    iss

    string

    issuer of the token (clientID provided by Annoto)

    Yes

    exp

    number

    expiration timestamp in seconds. Indicating when the user login session expires.

    Yes

    jti

    https://tools.ietf.org/html/rfc7519
    https://jwt.io/libraries

    number/string

    <?php
     require_once('./JWT.php'); // https://github.com/Annoto/jwt-php
    
    $issuedAt = time();
    $expire = $issuedAt + 60*20; // Adding 20 minutes
    $payload= array(
      "jti" => 1234,
      "name" => "Hen Eytan",
      "photoUrl" => "https://images.pexels.com/photos/101584/pexels-photo-101584.jpeg",
      "iss" => "zRCIsImlzcyI6Imh0dHA6XC9cL3d3dy5vcGVudS",
      "exp" => $expire
    );
    $secret = "4e54273d5d17859d464cb9bf";
    
    $jwtToken = JWT::encode($payload, $secret);
    ?>

    Course Dashboard Embed

    Quick Start

    The Annoto course dashboard can be embedded in any context as a web-component:

    It's built using StencilJS.

    Setup

    The Annoto course dashboard can be embedded in any context as a web-component. Simply add the following scripts:

    The following stylesheet:

    The course dashboard can now be embedded anywhere on the page using the html tag:

    Configuration Properties

    The dashboard web-component is built using StencilJS. Please refer to the official docs on more information on how web-components built using StencilJS can be integrated and used:

    The dashboard supports various configuration options:

    SSO Authentication

    To authenticate a user using SSO, the component exposes the following methods:

    'token' is same JWT token that is used for widget SSO. For more information please refer to .

    Browser Compatibility

    Annoto is compatible with all major evergreen browsers (Edge, Chrome, Mozilla, Safari).

    <head>
        <link href="https://dashboard.annoto.net/build/annotodashboard.css" rel="stylesheet">
    </head>
    <body>
        <script type="module" src="https://dashboard.annoto.net/build/annotodashboard.esm.js"></script>
        <script nomodule defer src="https://dashboard.annoto.net/build/annotodashboard.js"></script>
        
        <nnd-course-root responsive='false' history-type='compose' compose-history></nnd-course-root>
        
        <script>
            var courseEl = document.querySelector('nnd-course-root');
            courseEl.clientId = 'eyJhbGciOiJIUzI1...';
            courseEl.courseDetails = {
                id: 'ba028414-56f8', // your course identifier
                title: 'How to ebmed Course Dashboard',
            };
            
            var ssoToken = 'eyJhbGciOiJIUzI1...'; // user jwt token
            courseEl.addEventListener('nndReady', function() {
                courseEl.authenticateSSO(ssoToken);
            });
        </script>
    
    </body>
    https://stenciljs.com/docs/javascript
    https://stenciljs.com/docs/properties
    Single Sign On section
    <script type="module" src="https://dashboard.annoto.net/build/annotodashboard.esm.js"></script>
    <script nomodule defer src="https://dashboard.annoto.net/build/annotodashboard.js"></script>
    <link href="https://dashboard.annoto.net/build/annotodashboard.css" rel="stylesheet">
    <nnd-course-root></nnd-course-root>
    /**
     * The ClinetId key provided by Annoto (same key used for the Widget)
     */
    @Prop() clientId: string;
    /**
     * course details to load (same details used in 'group' configuration option of the widget).
     * https://annoto.github.io/widget-api/interfaces/Config.IConfig.html#group
     */
    @Prop() courseDetails: {
      id: string;
      title: string;
      description?: string;
      thumbnails?: {
        default: string;
        mobile?: string;
      };
    };
    /**
     * Deployment domain to use for the backend api.
     * @default 'eu.annoto.net'
     */
    @Prop() deploymentDomain: 'eu.annoto.net' | 'us.annoto.net';
    /**
     * Language code
     * @default 'en'
     */
    @Prop() locale: string;
    /**
     * if set to true, the dashboard be responsive with height and width set to 100%
     * and will have internal vertical scroll for the content.
     * default true
     */
    @Prop() responsive: boolean;
    /**
     * Browser history strategy to use
     * 'hash' - push routing state as by adding '#' component to the browser url
     * 'compose' - Navigate without changing the browser path url or history.
     *             This navigation strategy does not interfer with the host site navigation.
     * @default 'hash'
     */
    @Prop() historyType: 'hash' | 'compose';
    /**
     * if enabled, the dashboard navigation push history will be saved.
     * @default false
     */
    @Prop() composeHistory: boolean;
    /**
     * has effect only if historyType is set to 'compose'
     * if enabled, the dashboard nav paths will be saved in 'nnd' url query parameter.
     * @default true
     */
    @Prop() composeQueryParam: boolean;
    /**
     * If set to true, only accomulated analytcs will be displayed.
     * Per user anylitics will not be available.
     * @default false
     */
    @Prop() hidePersonalData: boolean;
    /**
     * If enabled preferences will not be available to the user.
     * @default false
     */
    @Prop() hidePreferences: boolean;
    /**
     * Launch source credentials for integration via proxy.
     * For example when the dashboard is do be embedded in LTI IFrame.
     */
    @Prop() launchSource: {
      consumerKey: string;
      origin: string;
    };
    async authenticateSSO(token: string): Promise<AuthCredentials> {}
    async logout(): Promise<void> {}

    Introduction

    Welcome to Annoto developers docs!

    If you are a developer, IT expert or anybody interested in integrating Annoto into your website or platform you are in the right place!

    For user guides and how to setup Annoto please head to our main docs site site.

    The core of Annoto is the Widget, it integrates Annoto without influencing website layout, as an overlay on top of your existing video player. We recommend reading the Widget getting started docs in any case, even if your preferred solution is Annoto Player or our Kaltura Player Plugin.