Custom Reader Implementation
Advanced users can create their own implementation of the web page reader. This allows you to create the entire player interface yourself and hide the default player. The player's functions are controlled through JavaScript using the API provided by the reader.
Preparation
Enable the reader normally according to the instructions on the previous page.
When the web reader is enabled, it automatically creates the window.AimReader namespace through which the reader can be controlled.
Reader Availability
The reader's namespace is only available after the reader's script has been loaded. You can check availability directly through the namespace or by listening for the 'AimReader.ready' event.
if (window.AimReader?.ready) {
// Reader is available
} else {
window.addEventListener('AimReader.ready', () => {
// Reader is available
}, { once: true });
}
Hiding the Default Player
When creating your own player interface, it's recommended to hide the default player completely. You can hide the player using the following CSS code.
.aim-player-host {
display: none !important;
}
Multiple players
If you have enabled multiple players in the Portal settings, you can identify the players by giving them a unique ID. The ID is defined with the data-aim-player-id attribute.
<article class="aim-readable" data-aim-player-id="my-article-1">
...
</article>
<article class="aim-readable" data-aim-player-id="my-article-2">
...
</article>
After this, you can use the ID in the API methods that support it. For example, you can start playing the second article by calling window.AimReader.play('my-article-2').
API
When the AimReader namespace is available, the following properties and functions can be used via the namespace, for example window.AimReader.play().
Properties
| Property | Type | Description |
|---|---|---|
active |
boolean, readonly |
Whether the reader is active, i.e., if it can play audio. Defaults to true unless you specifically deactivate the reader. |
ready |
true, readonly |
Whether the AimReader namespace is ready for use. |
speed |
number |
Reader's playback speed between 0.5 - 4.0. Defaults to 1.0 unless you've set a default speed in the Portal. You can change the speed by assigning a new value to this property. |
volume |
number |
Reader's volume between 0.0 - 1.0. Defaults to 1.0 unless you've set a default volume in the Portal. You can change the volume by assigning a new value to this property. |
Methods
| Method | Return Value | Description |
|---|---|---|
activate() |
void |
Activates the reader if it's deactivated. The reader is activated automatically when the script is loaded. |
deactivate() |
void |
Deactivates the reader. You can use this if you want to temporarily disable the reader completely on the current page. |
next(playerId?: string) |
Promise<void> |
Moves to play the next sentence. The Promise resolves when the next sentence's playback has started. |
pause() |
void |
Pauses playback. Playback can be resumed by calling play() or playPause(). |
play(playerId?: string) |
Promise<void> |
Starts playback or resumes paused playback. The Promise resolves when playback has started. |
playPause(playerId?: string) |
Promise<void> |
Toggles between pause and play states. The Promise resolves when playback has started. |
previous(playerId?: string) |
Promise<void> |
Moves to play the previous sentence. The Promise resolves when the previous sentence's playback has started. |
stop() |
void |
Stops playback completely and removes sentence highlighting. Playback cannot be resumed from the same point, only started from the beginning. |
Events
The reader sends some events, which you can listen to using the window.addEventListener function. The event name is of the form 'AimReader.<event>', for example 'AimReader.loading'. If the event has data, it can be found under the detail key. The data also includes playerId, which you can use to identify which player the event belongs to.
| Event name | Data | Description |
|---|---|---|
'AimReader.duration' |
{ durationRemaining: number, totalDuration: number, playerId: string } |
The estimated total duration of the audio and the remaining duration in seconds. This event is sent every second or whenever a duration changes. The playback speed affects the audio duration. |
'AimReader.error' |
{ error: string | false, playerId: string } |
An error occurred in the reader. Possible error values:- 'server-error' - The server does not respond normally.- 'no-text-content-found' - No text content found in the .aim-readable elements.- 'unknown-error' - Unknown error.- false - Previous error was cleared. |
'AimReader.loading' |
{ loading: boolean, playerId: string } |
The reader starts or stops loading. |
'AimReader.playback' |
{ action: 'play' | 'pause' | 'stop', playerId: string } |
The playback state changed. action tells what happened. |
'AimReader.ready' |
void |
The reader's namespace window.AimReader is ready for use. |
Example
<html>
<head>
<!-- Load the reader script -->
<script src="https://portal.aimater.com/static/reader/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.js"></script>
<script>
/**
* Enable the custom reader when the `AimReader` namespace is ready.
*/
function onAimReaderReady() {
// Make your custom reader visible.
const myCustomReader = document.getElementById('my-custom-reader');
myCustomReader?.removeAttribute('hidden');
// Show a loading icon when the reader is loading.
const loadingIcon = document.getElementById('loading-icon');
window.addEventListener(
'AimReader.loading',
(event) => {
if (event.detail.loading) {
loadingIcon?.removeAttribute('hidden');
} else {
loadingIcon?.setAttribute('hidden', '');
}
},
);
// Show an error message if the reader encounters an error.
const errorMessage = document.getElementById('error-message');
window.addEventListener(
'AimReader.error',
(event) => {
if (event.detail.error) {
errorMessage?.removeAttribute('hidden');
} else {
errorMessage?.setAttribute('hidden', '');
}
},
);
// Show the remaining duration of the audio.
const durationElement = document.getElementById('duration');
window.addEventListener(
'AimReader.duration',
(event) => {
durationElement.innerHTML = `Duration: ${event.detail.durationRemaining} s`;
},
);
// Set initial values for volume and speed inputs
const volumeInput = document.getElementById('volume-input');
const speedInput = document.getElementById('speed-input');
if (volumeInput) {
volumeInput.value = window.AimReader.volume;
}
if (speedInput) {
speedInput.value = window.AimReader.speed;
}
}
// Wait for the reader to be ready.
if (window.AimReader?.ready) {
onAimReaderReady();
} else {
window.addEventListener(
'AimReader.ready',
onAimReaderReady,
{ once: true },
);
}
</script>
<style>
/* Hide the default player. */
.aim-player-host {
display: none !important;
}
</style>
</head>
<body>
<aside id="my-custom-reader" hidden>
<div id="loading-icon" hidden></div>
<div id="error-message" hidden>Unexpected error occurred.</div>
<button type="button" onclick="window.AimReader.play()">Play</button>
<button type="button" onclick="window.AimReader.pause()">Pause</button>
<button type="button" onclick="window.AimReader.stop()">Stop</button>
<button type="button" onclick="window.AimReader.previous()">Previous</button>
<button type="button" onclick="window.AimReader.next()">Next</button>
<label for="volume-input">Volume</label>
<input
id="volume-input" type="range"
min="0" max="1" step="0.1"
oninput="window.AimReader.volume = this.value"
/>
<label for="speed-input">Speed</label>
<input
id="speed-input" type="range"
min="0.5" max="4" step="0.1"
oninput="window.AimReader.speed = this.value"
/>
<div id="duration">Duration: 0 s</div>
</aside>
<article class="aim-readable">
...
</article>
</body>
</html>