UNPKG

ngx-hotjar

Version:

A simple ng wrapper to load hotjar dependency by angular way

340 lines (328 loc) 11 kB
import { __awaiter } from 'tslib'; import { InjectionToken, inject, PLATFORM_ID, isDevMode, ɵɵdefineInjectable, ɵɵinject, Injectable, Inject, APP_BOOTSTRAP_LISTENER, APP_INITIALIZER, NgModule } from '@angular/core'; import { Router, NavigationEnd } from '@angular/router'; import { isPlatformBrowser, DOCUMENT, CommonModule } from '@angular/common'; /** * Check if this environment can access Window object and return window or null if false. */ function getWindow(platformId) { return (isPlatformBrowser(platformId)) ? window : null; } /** * Provide DOM Window reference or null if the environment is not a Browser. */ const WINDOW = new InjectionToken('hj-window', { factory: () => getWindow(inject(PLATFORM_ID)), }); /** * Check if there is some global function called gtag on Window object, or create an empty function to doesn't brake codes... */ function getHjFn(window) { // // h.hj=h.hj||function(){(h.hj.q=h.hj.q||[]).push(arguments)}; return (window) ? window.hj : null; } /** * Provides an injection token to access Google Analytics Gtag Function */ const NGX_HJ_FN = new InjectionToken('ngx-hj-fn', { providedIn: 'root', factory: () => getHjFn(inject(WINDOW)) }); /** * Provides a token to override default settings. You can use this token to enhance our library * and configure multiple sites/app on the same environment. */ const NGX_HOTJAR_SETTINGS_TOKEN = new InjectionToken('ngx-hotjar-settings', { factory: () => ({ trackingCode: '', version: 6, ennableTracing: false }) }); class NgxHotjarService { /** @ignore */ constructor( /** @ignore */ _hj, /** @ignore */ settings) { this._hj = _hj; this.settings = settings; } /** * Provide direct access to the `hj.*` static functions. If the desired function is not available on type definition, you can cast to `any` as following. * ```typescript (hjService.lib as any).myBrandNewStaticFn() ``` */ get lib() { return this._hj; } /** Expose Hotjar Function calls */ hj(...args) { try { this._hj(...args); } catch (err) { if (isDevMode() || this.settings.ennableTracing) { console.error(err.message); } } } /** * Fires an PageView event on Hotjar. Use this method to trigger an virtual url path. The same as * ```typescript hj('vpv', path) ``` */ virtualPageView(path) { this.hj('vpv', path); } /** * Fires an event on Hotjar. Use this method to trigger events on forms and start video recordings. Same as * ```typescript hj('trigger', path) ``` */ trigger(path) { this.hj('trigger', path); } tagRecording(tagOrCollection, ...tags) { // Retrocompatibility if (!Array.isArray(tagOrCollection)) { tagOrCollection = [tagOrCollection]; } this.hj('tagRecording', tagOrCollection.concat(...tags)); } /** * This option is available in case you need to set up page change tracking manually * within your app's router. * ```typescript hj('stateChange', path) ``` */ stateChange(path) { this.hj('stateChange', path); } /** * Signals form submission success * ```typescript hj('formSubmitSuccessful'); ``` */ formSubmitSuccessful() { this.hj('formSubmitSuccessful'); } /** * Signals form submission failure * ```typescript hj('formSubmitFailed'); ``` */ formSubmitFailed() { this.hj('formSubmitFailed'); } } NgxHotjarService.ɵprov = ɵɵdefineInjectable({ factory: function NgxHotjarService_Factory() { return new NgxHotjarService(ɵɵinject(NGX_HJ_FN), ɵɵinject(NGX_HOTJAR_SETTINGS_TOKEN)); }, token: NgxHotjarService, providedIn: "root" }); NgxHotjarService.decorators = [ { type: Injectable, args: [{ providedIn: 'root' },] } ]; NgxHotjarService.ctorParameters = () => [ { type: undefined, decorators: [{ type: Inject, args: [NGX_HJ_FN,] }] }, { type: undefined, decorators: [{ type: Inject, args: [NGX_HOTJAR_SETTINGS_TOKEN,] }] } ]; /** * Provide a DI Configuration to attach Hotjar Trigger to Router Events at Angular Startup Cycle. */ const NGX_HOTJAR_ROUTER_INITIALIZER_PROVIDER = { provide: APP_BOOTSTRAP_LISTENER, multi: true, useFactory: HotjarRouterInitializer, deps: [ NgxHotjarService ] }; /** * Attach a listener to `NavigationEnd` Router event. So, every time Router finish the page resolution it should call `NavigationEnd` event. * We assume that NavigationEnd is the final page resolution and call Hotjar `stateChange` command. * * To avoid double binds, we also destroy the subscription when de Bootstrap Component is destroied. But, we don't know for sure * that this strategy does not cause double bind on multiple bootstrap components. * * We are using de component's injector reference to resolve Router, sou I hope there is no problem w/ double bing. * * If you have this problem, I encourage not Use NgxHotjarRouterModule and atach the listener on AppComponent initialization. */ function HotjarRouterInitializer(hjService) { return (c) => __awaiter(this, void 0, void 0, function* () { const router = c.injector.get(Router); const subs = router .events .subscribe(event => { if (event instanceof NavigationEnd) { hjService.stateChange(event.urlAfterRedirects); } }); // Cleanup c.onDestroy(() => subs.unsubscribe()); }); } /** * Provides a TOKEN to manually configure Hojtar tracking code by angular way. */ const NGX_HOTJAR_INITIALIZER_PROVIDER = { provide: APP_INITIALIZER, multi: true, useFactory: hotjarInitializer, deps: [ NGX_HOTJAR_SETTINGS_TOKEN, DOCUMENT, WINDOW ] }; /** * Configuration Factory to create hotjar install script tag and attache on DOM at angular initialization. */ function hotjarInitializer(settings, document, window) { return () => __awaiter(this, void 0, void 0, function* () { if (!settings.trackingCode) { if (isDevMode()) { console.error('Empty tracking code for Hotjar. Make sure to provide one when initializing NgxHotjarModule.'); } return; } if (!document) { if (isDevMode()) { console.error('Was not possible to access `document` instance. Make shure this environment works on a Broser like API'); } return; } if (!window) { if (isDevMode()) { console.error('Was not possible to access `window` api. Make sure this environment works like a browser.'); } return; } Object.defineProperty(window, 'hj', { value: (window.hj || function () { (window.hj.q = window.hj.q || []).push(arguments); }), configurable: true, writable: true }); Object.defineProperty(window, '_hjSettings', { value: { hjid: settings.trackingCode, hjsv: (settings.version || 6) }, configurable: true, writable: true }); const head = document.querySelector('head'), script = document.createElement('script'), uri = `https://static.hotjar.com/c/hotjar-${window._hjSettings.hjid}.js?sv=${window._hjSettings.hjsv}`; script.async = true; script.src = (settings.uri || uri); head.appendChild(script); }); } /** * Install Hotjar scripts on Angular Startup life cycle if this environment is a Browser, otherwise just ignore this step. * * You shall add this module on the Gighest level module of your application, aka `AppModule`. When * setup this module, you also have to provide you hotjat tracking code and the version of script. The default version * is 6. We do not recoment to expose the tracking code in the repository, so please, use angular environment variable. * * ## Exemple of Use * ```typescript \@NgModule({ ... imports: [ ... NgxHotjarModule.forRoot(envorinment.hj) // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ] }) ``` */ class NgxHotjarModule { /** * Setup global settings and provide Hotjar Services. * * You private tracking code. This tracking code is also known as the same number as `SITE ID` inside Hotjar Dashboard. */ static forRoot(trackingCode, version = 6, uri) { return { ngModule: NgxHotjarModule, providers: [ { provide: NGX_HOTJAR_SETTINGS_TOKEN, useValue: { trackingCode, version, uri } }, NGX_HOTJAR_INITIALIZER_PROVIDER ] }; } } NgxHotjarModule.decorators = [ { type: NgModule, args: [{ imports: [], declarations: [], exports: [] },] } ]; /** * Attach a listener to `NavigationEnd` Router event. So, every time Router finish the page resolution it should call `NavigationEnd` event. * We assume that NavigationEnd is the final page resolution and call Hotjar `stateChange` command. * * To avoid double binds, we also destroy the subscription when de Bootstrap Component is destroied. But, we don't know for sure * that this strategy does not cause double bind on multiple bootstrap components. * * We are using de component's injector reference to resolve Router, sou I hope there is no problem w/ double bing. * * If you have this problem, I encourage not Use NgxHotjarRouterModule and atach the listener on AppComponent initialization. * * This Module is just a sugar for: * ```typescript constructor(private router: Router) {} ... ngOnInit() { ... this.router .events .pipe(takeUntil(this.onDestroy$)) .subscribe(event => { if (event instanceof NavigationEnd) { hjService.pageView(event.urlAfterRedirects, undefined); } }); ``` */ class NgxHotjarRouterModule { } NgxHotjarRouterModule.decorators = [ { type: NgModule, args: [{ imports: [ CommonModule, NgxHotjarModule ], providers: [ NGX_HOTJAR_ROUTER_INITIALIZER_PROVIDER ], declarations: [] },] } ]; /* * Public API Surface of ngx-hotjar */ /** * Generated bundle index. Do not edit. */ export { HotjarRouterInitializer, NGX_HJ_FN, NGX_HOTJAR_INITIALIZER_PROVIDER, NGX_HOTJAR_ROUTER_INITIALIZER_PROVIDER, NGX_HOTJAR_SETTINGS_TOKEN, NgxHotjarModule, NgxHotjarRouterModule, NgxHotjarService, WINDOW, getHjFn, getWindow, hotjarInitializer }; //# sourceMappingURL=ngx-hotjar.js.map