Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Allow window to be truly undefined #895

Merged
merged 5 commits into from
Nov 15, 2023
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
2 changes: 1 addition & 1 deletion src/autocapture-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ export function shouldCaptureDomEvent(
event: Event,
autocaptureConfig: AutocaptureConfig | undefined = undefined
): boolean {
if (!el || isTag(el, 'html') || !isElementNode(el)) {
if (!window || !el || isTag(el, 'html') || !isElementNode(el)) {
return false
}

Expand Down
14 changes: 10 additions & 4 deletions src/autocapture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ const autocapture = {

_extractCustomPropertyValue: function (customProperty: AutoCaptureCustomProperty): string {
const propValues: string[] = []
_each(document.querySelectorAll(customProperty['css_selector']), function (matchedElem) {
_each(document?.querySelectorAll(customProperty['css_selector']), function (matchedElem) {
let value

if (['input', 'select'].indexOf(matchedElem.tagName.toLowerCase()) > -1) {
Expand All @@ -153,7 +153,7 @@ const autocapture = {
const props: Properties = {} // will be deleted
_each(this._customProperties, (customProperty) => {
_each(customProperty['event_selectors'], (eventSelector) => {
const eventElements = document.querySelectorAll(eventSelector)
const eventElements = document?.querySelectorAll(eventSelector)
_each(eventElements, (eventElement) => {
if (_includes(targetElementList, eventElement) && shouldCaptureElement(eventElement)) {
props[customProperty['name']] = this._extractCustomPropertyValue(customProperty)
Expand Down Expand Up @@ -271,12 +271,18 @@ const autocapture = {
// only reason is to stub for unit tests
// since you can't override window.location props
_navigate: function (href: string): void {
if (!window) {
return
}
window.location.href = href
},

_addDomEventHandlers: function (instance: PostHog): void {
if (!window || !document) {
return
}
const handler = (e: Event) => {
e = e || window.event
e = e || window?.event
this._captureEvent(e, instance)
}
_register_event(document, 'submit', handler, false, true)
Expand Down Expand Up @@ -359,7 +365,7 @@ const autocapture = {
},

isBrowserSupported: function (): boolean {
return _isFunction(document.querySelectorAll)
return _isFunction(document?.querySelectorAll)
},
}

Expand Down
13 changes: 8 additions & 5 deletions src/extensions/exception-autocapture/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ export const extendPostHog = (instance: PostHog, response: DecideResponse) => {
export class ExceptionObserver {
instance: PostHog
remoteEnabled: boolean | undefined
private originalOnErrorHandler: typeof window['onerror'] | null | undefined = undefined
private originalOnUnhandledRejectionHandler: typeof window['onunhandledrejection'] | null | undefined = undefined
private originalOnErrorHandler: Window['onerror'] | null | undefined = undefined
private originalOnUnhandledRejectionHandler: Window['onunhandledrejection'] | null | undefined = undefined

private errorsToIgnore: RegExp[] = []

Expand All @@ -28,7 +28,7 @@ export class ExceptionObserver {
}

startCapturing() {
if (!this.isEnabled() || (window.onerror as any)?.__POSTHOG_INSTRUMENTED__) {
if (!window || !this.isEnabled() || (window.onerror as any)?.__POSTHOG_INSTRUMENTED__) {
return
}

Expand Down Expand Up @@ -56,7 +56,7 @@ export class ExceptionObserver {
const errorProperties: ErrorProperties = unhandledRejectionToProperties(args)
this.sendExceptionEvent(errorProperties)

if (this.originalOnUnhandledRejectionHandler) {
if (window && this.originalOnUnhandledRejectionHandler) {
// eslint-disable-next-line prefer-rest-params
return this.originalOnUnhandledRejectionHandler.apply(window, args)
}
Expand All @@ -71,6 +71,9 @@ export class ExceptionObserver {
}

stopCapturing() {
if (!window) {
return
}
if (!_isUndefined(this.originalOnErrorHandler)) {
window.onerror = this.originalOnErrorHandler
this.originalOnErrorHandler = null
Expand All @@ -85,7 +88,7 @@ export class ExceptionObserver {
}

isCapturing() {
return !!(window.onerror as any)?.__POSTHOG_INSTRUMENTED__
return !!(window?.onerror as any)?.__POSTHOG_INSTRUMENTED__
}

isEnabled() {
Expand Down
6 changes: 3 additions & 3 deletions src/extensions/replay/sessionrecording.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ export class SessionRecording {
private get isRecordingEnabled() {
const enabled_server_side = !!this.instance.get_property(SESSION_RECORDING_ENABLED_SERVER_SIDE)
const enabled_client_side = !this.instance.config.disable_session_recording
return enabled_server_side && enabled_client_side
return window && enabled_server_side && enabled_client_side
}

private get isConsoleLogCaptureEnabled() {
Expand Down Expand Up @@ -192,7 +192,7 @@ export class SessionRecording {
this.stopRrweb = undefined
this.receivedDecide = false

window.addEventListener('beforeunload', () => {
window?.addEventListener('beforeunload', () => {
this._flushBuffer()
})

Expand Down Expand Up @@ -507,7 +507,7 @@ export class SessionRecording {
// so we catch all errors.
try {
if (eventName === '$pageview') {
const href = this._maskUrl(window.location.href)
const href = window ? this._maskUrl(window.location.href) : ''
if (!href) {
return
}
Expand Down
13 changes: 11 additions & 2 deletions src/extensions/surveys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ import {
} from '../posthog-surveys-types'

import { _isUndefined } from '../utils/type-utils'
import { window, document } from '../utils/globals'
import { window as _window, document as _document } from '../utils/globals'

// We cast the types here which is dangerous but protected by the top level generateSurveys call
const window = _window as Window & typeof globalThis
const document = _document as Document

const satisfiedEmoji =
'<svg class="emoji-svg" xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" width="48"><path d="M626-533q22.5 0 38.25-15.75T680-587q0-22.5-15.75-38.25T626-641q-22.5 0-38.25 15.75T572-587q0 22.5 15.75 38.25T626-533Zm-292 0q22.5 0 38.25-15.75T388-587q0-22.5-15.75-38.25T334-641q-22.5 0-38.25 15.75T280-587q0 22.5 15.75 38.25T334-533Zm146 272q66 0 121.5-35.5T682-393h-52q-23 40-63 61.5T480.5-310q-46.5 0-87-21T331-393h-53q26 61 81 96.5T480-261Zm0 181q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-400Zm0 340q142.375 0 241.188-98.812Q820-337.625 820-480t-98.812-241.188Q622.375-820 480-820t-241.188 98.812Q140-622.375 140-480t98.812 241.188Q337.625-140 480-140Z"/></svg>'
Expand Down Expand Up @@ -897,14 +901,19 @@ function showQuestion(n: number, surveyId: string) {
function nextQuestion(currentQuestionIdx: number, surveyId: string) {
// figure out which tab to display
const tabs = document
.getElementsByClassName(`PostHogSurvey${surveyId}`)[0]
?.getElementsByClassName(`PostHogSurvey${surveyId}`)[0]
?.shadowRoot?.querySelectorAll('.tab') as NodeListOf<HTMLElement>

tabs[currentQuestionIdx].style.display = 'none'
showQuestion(currentQuestionIdx + 1, surveyId)
}

// This is the main exported function
export function generateSurveys(posthog: PostHog) {
// NOTE: Important to ensure we never try and run surveys without a window environment
if (!document || !window) {
return
}
callSurveys(posthog, true)

// recalculate surveys every 3 seconds to check if URL or selectors have changed
Expand Down
20 changes: 13 additions & 7 deletions src/extensions/toolbar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { window, document, assignableWindow } from '../utils/globals'

// TRICKY: Many web frameworks will modify the route on load, potentially before posthog is initialized.
// To get ahead of this we grab it as soon as the posthog-js is parsed
const STATE_FROM_WINDOW = window.location
const STATE_FROM_WINDOW = window?.location
? _getHashParam(window.location.hash, '__posthog') || _getHashParam(location.hash, 'state')
: null

Expand Down Expand Up @@ -40,10 +40,16 @@ export class Toolbar {
* 2. From session storage under the key `toolbarParams` if the toolbar was initialized on a previous page
*/
maybeLoadToolbar(
location = window.location,
location: Location | undefined = undefined,
localStorage: Storage | undefined = undefined,
history = window.history
history: History | undefined = undefined
): boolean {
if (!window || !document) {
return false
}
location = location ?? window.location
history = history ?? window.history

try {
// Before running the code we check if we can access localStorage, if not we opt-out
if (!localStorage) {
Expand All @@ -55,7 +61,7 @@ export class Toolbar {
}

// If localStorage was undefined, and localStorage is supported we set the default value
localStorage = window.localStorage
localStorage = window?.localStorage
}

/**
Expand Down Expand Up @@ -114,7 +120,7 @@ export class Toolbar {
}

loadToolbar(params?: ToolbarParams): boolean {
if (assignableWindow['_postHogToolbarLoaded']) {
if (!window || assignableWindow['_postHogToolbarLoaded']) {
return false
}
// only load the toolbar once, even if there are multiple instances of PostHogLib
Expand Down Expand Up @@ -164,9 +170,9 @@ export class Toolbar {

/** @deprecated Use "maybeLoadToolbar" instead. */
maybeLoadEditor(
location = window.location,
location: Location | undefined = undefined,
localStorage: Storage | undefined = undefined,
history = window.history
history: History | undefined = undefined
): boolean {
return this.maybeLoadToolbar(location, localStorage, history)
}
Expand Down
4 changes: 2 additions & 2 deletions src/gdpr-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,11 +158,11 @@ function _getStorageValue(token: string, options: GDPROptions) {
function _hasDoNotTrackFlagOn(options: GDPROptions) {
if (options && options.respectDnt) {
const win = (options && options.window) || window
const nav = win['navigator'] || {}
const nav = win?.navigator
let hasDntOn = false
_each(
[
nav['doNotTrack'], // standard
nav?.doNotTrack, // standard
(nav as any)['msDoNotTrack'],
(win as any)['doNotTrack'],
],
Expand Down
15 changes: 6 additions & 9 deletions src/loader-recorder-v2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -454,14 +454,11 @@ export const getRecordNetworkPlugin: (options?: NetworkRecordOptions) => RecordP
}

// rrweb/networ@1 ends
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
window.rrweb = { record: rrwebRecord, version: 'v2', rrwebVersion: version }
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
window.rrwebConsoleRecord = { getRecordConsolePlugin }
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
window.getRecordNetworkPlugin = getRecordNetworkPlugin

if (window) {
;(window as any).rrweb = { record: rrwebRecord, version: 'v2', rrwebVersion: version }
;(window as any).rrwebConsoleRecord = { getRecordConsolePlugin }
;(window as any).getRecordNetworkPlugin = getRecordNetworkPlugin
}

export default rrwebRecord
10 changes: 4 additions & 6 deletions src/loader-recorder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,9 @@ import { getRecordConsolePlugin } from 'rrweb-v1/es/rrweb/packages/rrweb/src/plu

import { window } from './utils/globals'

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
window.rrweb = { record: rrwebRecord, version: 'v1', rrwebVersion: version }
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
window.rrwebConsoleRecord = { getRecordConsolePlugin }
if (window) {
;(window as any).rrweb = { record: rrwebRecord, version: 'v1', rrwebVersion: version }
;(window as any).rrwebConsoleRecord = { getRecordConsolePlugin }
}

export default rrwebRecord
7 changes: 3 additions & 4 deletions src/loader-surveys.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { generateSurveys } from './extensions/surveys'

import { _isUndefined } from './utils/type-utils'
import { window } from './utils/globals'

const win: Window & typeof globalThis = _isUndefined(window) ? ({} as typeof window) : window

;(win as any).extendPostHogWithSurveys = generateSurveys
if (window) {
;(window as any).extendPostHogWithSurveys = generateSurveys
}

export default generateSurveys
24 changes: 13 additions & 11 deletions src/page-view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export class PageViewManager {

_createPageViewData(): PageViewData {
return {
pathname: window.location.pathname,
pathname: window?.location.pathname ?? '',
}
}

Expand Down Expand Up @@ -134,31 +134,33 @@ export class PageViewManager {
}

startMeasuringScrollPosition() {
window.addEventListener('scroll', this._updateScrollData)
window.addEventListener('scrollend', this._updateScrollData)
window.addEventListener('resize', this._updateScrollData)
window?.addEventListener('scroll', this._updateScrollData)
window?.addEventListener('scrollend', this._updateScrollData)
window?.addEventListener('resize', this._updateScrollData)
}

stopMeasuringScrollPosition() {
window.removeEventListener('scroll', this._updateScrollData)
window.removeEventListener('scrollend', this._updateScrollData)
window.removeEventListener('resize', this._updateScrollData)
window?.removeEventListener('scroll', this._updateScrollData)
window?.removeEventListener('scrollend', this._updateScrollData)
window?.removeEventListener('resize', this._updateScrollData)
}

_scrollHeight(): number {
return Math.max(0, window.document.documentElement.scrollHeight - window.document.documentElement.clientHeight)
return window
? Math.max(0, window.document.documentElement.scrollHeight - window.document.documentElement.clientHeight)
: 0
}

_scrollY(): number {
return window.scrollY || window.pageYOffset || window.document.documentElement.scrollTop || 0
return window ? window.scrollY || window.pageYOffset || window.document.documentElement.scrollTop || 0 : 0
}

_contentHeight(): number {
return window.document.documentElement.scrollHeight || 0
return window?.document.documentElement.scrollHeight || 0
}

_contentY(): number {
const clientHeight = window.document.documentElement.clientHeight || 0
const clientHeight = window?.document.documentElement.clientHeight || 0
return this._scrollY() + clientHeight
}
}
Expand Down
Loading
Loading