Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 1 addition & 32 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import './patch';
import 'reflect-metadata';
import 'regenerator-runtime/runtime';

import DependencyContainer from 'tsyringe/dist/typings/types/dependency-container';
import { bootstrap } from './bootstrap';
import { container } from 'tsyringe';
import { inject } from './inject';
import { tw2patch } from './tw2-patch';
import { TWCalcPublicApi } from './tw-calc.types';

Expand All @@ -14,34 +14,3 @@ container.register('localStorage', { useValue: localStorage });
container.register('tw2patch', { useValue: tw2patch });

inject<TWCalcPublicApi>(bootstrap, window, location);

// inject script when the game is ready
function inject<T>(
bootstrapper: (dependencyContainer: DependencyContainer) => T,
window: Window,
location: Location,
): NodeJS.Timeout {
const injectInterval = setInterval(() => {
if (!isGameReady(window)) {
return;
}
// clear the injector interval
clearInterval(injectInterval);
// inject the script
window['TW_Calc'] = injectScript<T>(bootstrapper, location);
}, 250);
return injectInterval;
}

function isGameReady(window: Window): boolean {
// jquery must be loaded
return typeof window['$'] !== 'undefined';
}

function injectScript<T>(bootstrapper: (dependencyContainer: DependencyContainer) => T, location: Location): T {
const { href } = location;
if ((href.indexOf('.the-west.') != -1 || href.indexOf('.tw.innogames.') != -1) && href.indexOf('game.php') != -1) {
return bootstrapper(container);
}
throw new Error('TW-Calc must be loaded in the game!');
}
55 changes: 55 additions & 0 deletions src/inject.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { inject, isAlreadyInjected, isGameReady } from './inject';

describe('inject', () => {
let win: Partial<Window> & { $?: unknown; TW_Calc?: unknown };
let location: Location;
let bootstrapper: jasmine.Spy;

beforeEach(() => {
jasmine.clock().install();
bootstrapper = jasmine.createSpy('bootstrap').and.returnValue({});
location = { href: 'https://en.the-west.net/game.php' } as Location;
win = {};
});

afterEach(() => {
jasmine.clock().uninstall();
});

it('isGameReady reflects jQuery presence', () => {
expect(isGameReady({} as Window)).toBe(false);
expect(isGameReady({ $: () => undefined } as unknown as Window)).toBe(true);
});

it('isAlreadyInjected reflects TW_Calc presence', () => {
expect(isAlreadyInjected({} as Window)).toBe(false);
expect(isAlreadyInjected({ TW_Calc: {} } as unknown as Window)).toBe(true);
});

it('does not bootstrap until the game is ready', () => {
inject(bootstrapper, win as Window, location);
jasmine.clock().tick(1000);

expect(bootstrapper).not.toHaveBeenCalled();
});

it('bootstraps once when the game is ready', () => {
win.$ = () => undefined;

inject(bootstrapper, win as Window, location);
jasmine.clock().tick(250);

expect(bootstrapper).toHaveBeenCalledTimes(1);
expect(win.TW_Calc).toBeDefined();
});

it('does not bootstrap again when the script is already injected', () => {
win.$ = () => undefined;
win.TW_Calc = {}; // a previous injection already set this

inject(bootstrapper, win as Window, location);
jasmine.clock().tick(1000);

expect(bootstrapper).not.toHaveBeenCalled();
});
});
42 changes: 42 additions & 0 deletions src/inject.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import DependencyContainer from 'tsyringe/dist/typings/types/dependency-container';
import { container } from 'tsyringe';

export function isGameReady(window: Window): boolean {
// jquery must be loaded
return typeof window['$'] !== 'undefined';
}

export function isAlreadyInjected(window: Window): boolean {
return typeof window['TW_Calc'] !== 'undefined';
}

// inject script when the game is ready
export function inject<T>(
bootstrapper: (dependencyContainer: DependencyContainer) => T,
window: Window,
location: Location,
): ReturnType<typeof setInterval> {
const injectInterval = setInterval(() => {
if (!isGameReady(window)) {
return;
}
// clear the injector interval
clearInterval(injectInterval);
// the script may be injected more than once on the page (e.g. re-injection or an
// iframe); only bootstrap once, otherwise TheWestApi.register throws "already registered"
if (isAlreadyInjected(window)) {
return;
}
// inject the script
window['TW_Calc'] = injectScript<T>(bootstrapper, location);
}, 250);
return injectInterval;
}

export function injectScript<T>(bootstrapper: (dependencyContainer: DependencyContainer) => T, location: Location): T {
const { href } = location;
if ((href.indexOf('.the-west.') != -1 || href.indexOf('.tw.innogames.') != -1) && href.indexOf('game.php') != -1) {
return bootstrapper(container);
}
throw new Error('TW-Calc must be loaded in the game!');
}
Loading