Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
<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);Annoto plugin integration for the Kaltura player
Annoto plugin integration for the Kaltura player
flashvarskWidget.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',
})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><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>kWidget.addReadyCallback(function (playerId) {
player.kBind('annotoPluginReady', function (annotoApi) {
// Make use of the Annoto API
});
});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);
});
});loadWidget(), the widget is loaded in that player.player.setup();
player.close();
player.reset();
player.setupWidget();
player.loadWidget();
player.destroyWidget();<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>eyJhbGciOiJ...<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><body>
<script src="https://player.annoto.net/index.js"></script>
</body>
<script>
// Annoto modules is available at 'nn' global object:
// nn.annotoPlayer
</script>$ npm install @annoto/playerrequirejs(["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);<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> {}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
});
});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);<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"
}
}.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;
}<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_linkconst 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[];
}
}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);
});
$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"
}<?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);
?>

