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;
}

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() 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() Promise<void> Starts playback or resumes paused playback. The Promise resolves when playback has started.
playPause() Promise<void> Toggles between pause and play states. The Promise resolves when playback has started.
previous() 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.

Event name Data Description
'AimReader.duration' { totalDuration: number, durationRemaining: number } 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 } 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.
- false - Previous error was cleared.
'AimReader.loading' { loading: boolean } The reader starts or stops loading.
'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 reader is ready.
             */
            function onReaderReady() {
                // 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) {
                onReaderReady();
            } else {
                window.addEventListener(
                    'AimReader.ready',
                    onReaderReady,
                    { 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>