Skip to content

auth() -> connectedCallback() -> tpen-project-loaded -> CheckPermissions -> render() -> register event listeners -> disconnectedCallback() Audit Report #404

@thehabes

Description

@thehabes

TPEN3 Web Component Lifecycle Audit Report

Discovering the scope of #388, #398, #400, & #401.

Overview

This report audits all web components in the TPEN3 Interfaces codebase against the expected lifecycle pattern:

auth() -> connectedCallback() -> tpen-project-loaded -> CheckPermissions -> render() -> register event listeners -> disconnectedCallback()

Expected Lifecycle Elements:

  1. auth(): Authentication attachment via TPEN.attachAuthentication()
  2. connectedCallback(): Standard web component lifecycle hook
  3. tpen-project-loaded: Event listener for project data availability
  4. CheckPermissions: Permission validation before rendering
  5. render(): UI rendering method
  6. Event listener registration: Setting up event listeners (various naming conventions)
  7. disconnectedCallback(): Cleanup of event listeners and resources

Component Inventory

Components Directory (Excluding Classroom)

Component File Path
CheckPermissions components/check-permissions/checkPermissions.js
PermissionMatchElement components/check-permissions/permission-match-element.js
PermissionMatch components/check-permissions/permission-match.js
DeclineProject components/decline-project/index.js
DefaultTranscribe components/default-transcribe/index.js
FeedbackButton components/feedback-button/index.js
Feedback components/feedback/index.js
Alert components/gui/alert/Alert.js
AlertContainer components/gui/alert/AlertContainer.js
Confirm components/gui/confirm/Confirm.js
ConfirmContainer components/gui/confirm/ConfirmContainer.js
Header components/gui/site/Header.js
NoAuthHeader components/gui/site/NoAuthHeader.js
Page components/gui/site/Page.js
Footer components/gui/site/Footer.js
Toast components/gui/toast/Toast.js
ToastContainer components/gui/toast/ToastContainer.js
Card components/gui/card/Card.js
ProjectList components/projects/index.js
ProjectListView components/projects/project-list-view.js
ProjectListWrite components/projects/project-list-write.js
ProjectHeader components/projects/project-header.js
ListNavigation components/projects/list-navigation.js
PublicLogin components/public-login/index.js
PublicUserProfile components/public-user-profile/index.js
UserProfile components/user-profile/index.js
ContributionActivity components/user-profile/contributionActivity.js
Report components/user-profile/report.js
UserStats components/user-profile/userStats.js
ProjectCollaborators components/project-collaborators/index.js
MemberInvitation components/member-invitation/index.js
LineText components/line-text/index.js
LineImage components/line-image/index.js
LineHistory components/line-history/index.js
QuickGuide components/quick-guide/index.js
SplitScreen components/split-screen/index.js
RolesHandler components/roles-handler/index.js
ManageRole components/manage-role/index.js
LeaveProject components/leave-project/index.js
NavigationManager components/navigation-manager/index.js
ContinueWorking components/continue-working/index.js
CreateColumn components/create-column/index.js
ProjectMetadata components/project-metadata/index.js
ProjectPermissions components/project-permissions/index.js
ProjectLayers components/project-layers/index.js
ProjectDetails components/project-details/index.js
ProjectOptions components/project-options/index.js
ProjectExport components/project-export/index.js
ProjectTools components/project-tools/index.js
CopyExistingProject components/copy-existing-project/index.js
CopyProjectWithCustomization components/copy-project-with-customization/index.js
CopyProjectWithGroupMember components/copy-project-with-group-member/index.js
CopyProjectWithoutAnnotations components/copy-project-without-annotations/index.js
ImportImage components/import-image/index.js
ImportProject components/import-project/index.js
ManageLayers components/manage-layers/index.js
ManagePages components/manage-pages/index.js
LayerSelector components/layer-selector/index.js
ColumnSelector components/column-selector/index.js
PageTool components/page-tool/index.js
MagnifierTool components/magnifier-tool/index.js
QuicktypeManager components/quicktype-manager/index.js
QuicktypeTool components/quicktype-tool/index.js
QuicktypeEditorDialog components/quicktype-tool/quicktype-editor-dialog.js
WorkspaceTools components/workspace-tools/index.js
SplitscreenTool components/splitscreen-tool/index.js
TranscriptionBlock components/transcription-block/index.js
SimpleTranscription components/simple-transcription/index.js
ReadOnlyViewTranscribe components/read-only-transcribe/index.js
UpdateMetadata components/update-metadata/index.js
FixedExplanatoryGuide components/explanatory-guide/fixedGuideComponent.js
RelativeExplanatoryGuide components/explanatory-guide/relativeGuideComponent.js
AnnotoriousAnnotator (plain) components/annotorious-annotator/plain.js
AnnotoriousAnnotator (line-parser) components/annotorious-annotator/line-parser.js
LegacyAnnotator components/legacy-annotator/plain.js
TpenQuickType components/quicktype/quicktype.js
NewAction components/new-action.js

Interfaces Directory

Interface File Path
TranscriptionInterface interfaces/transcription/index.js
ManageColumnsInterface interfaces/manage-columns/index.js
ManageProjectInterface interfaces/manage-project/index.js
TpenCustomProperty interfaces/custom/index.js
ImportTpen28Interface interfaces/import-tpen28/index.js
NavigationInterface interfaces/navigation/index.js
QuicktypeInterface interfaces/quicktype/index.js

Detailed Component Analysis

Legend

  • ✅ Implements correctly
  • ⚠️ Partial implementation / Inconsistent naming
  • ❌ Missing
  • 🔶 Memory leak risk
  • N/A Not applicable for this component type

Category 1: Project-Dependent Components (Require Full Lifecycle)

These components depend on project data and should follow the complete lifecycle pattern.

1. DefaultTranscribe (components/default-transcribe/index.js)

Element Status Notes
auth() TPEN.attachAuthentication(this) in connectedCallback
connectedCallback() Present
tpen-project-loaded eventDispatcher.on('tpen-project-loaded', (ev) => {...})
CheckPermissions ⚠️ Uses CheckPermissions.checkAllAccess('LINE', 'TEXT') but with incorrect permissions check pattern
render() Present (inline in connectedCallback after project loads)
Event listeners ⚠️ Uses attachEventListeners() - inconsistent naming
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK - registers global event listeners

Memory Leak Issues:

  • Registers tpen-project-loaded listener without cleanup
  • Multiple resize observers without cleanup
  • Event listeners on elements without removal

2. ProjectCollaborators (components/project-collaborators/index.js)

Element Status Notes
auth() TPEN.attachAuthentication(this) in connectedCallback
connectedCallback() Present
tpen-project-loaded TPEN.eventDispatcher.on('tpen-project-loaded', () => this.render())
CheckPermissions Uses CheckPermissions.checkViewAccess('GROUP') and checkEditAccess('PROJECT', 'MEMBERS')
render() Present
Event listeners ⚠️ No dedicated setup method - inline event registration
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

Memory Leak Issues:

  • Global event listener for tpen-project-loaded not cleaned up

3. MemberInvitation (components/member-invitation/index.js)

Element Status Notes
auth() TPEN.attachAuthentication(this) in connectedCallback
connectedCallback() Present
tpen-project-loaded TPEN.eventDispatcher.on("tpen-project-loaded", () => this.render())
CheckPermissions Uses CheckPermissions.checkEditAccess("PROJECT", "MEMBERS")
render() Present with proper permission gating
Event listeners ⚠️ Inline event registration, no dedicated method
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

4. LineText (components/line-text/index.js)

Element Status Notes
auth() TPEN.attachAuthentication(this) in constructor
connectedCallback() Present
tpen-project-loaded eventDispatcher.on("tpen-project-loaded", () => this.lineIndex && this.render())
CheckPermissions ⚠️ No permission checks before rendering
render() Present
Event listeners ⚠️ Uses attachEventListeners()
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

Memory Leak Issues:

  • Global event listeners (tpen-project-loaded, tpen-line-loaded)
  • MutationObserver on textarea without cleanup

5. LineImage (components/line-image/index.js)

Element Status Notes
auth() TPEN.attachAuthentication(this) in connectedCallback
connectedCallback() Present
tpen-project-loaded Present
CheckPermissions No permission checks
render() Present
Event listeners ⚠️ Inline registration
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

6. LineHistory (components/line-history/index.js)

Element Status Notes
auth() TPEN.attachAuthentication(this) in connectedCallback
connectedCallback() Present
tpen-project-loaded TPEN.eventDispatcher.on("tpen-project-loaded", () => this.render())
CheckPermissions No permission checks
render() Present
Event listeners ⚠️ Inline registration
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

7. ManageRole (components/manage-role/index.js)

Element Status Notes
auth() TPEN.attachAuthentication(this) in connectedCallback
connectedCallback() Present
tpen-project-loaded TPEN.eventDispatcher.on("tpen-project-loaded", () => this.render())
CheckPermissions No permission checks
render() Present
Event listeners ⚠️ Uses registerEventListeners() - inconsistent naming
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

8. LeaveProject (components/leave-project/index.js)

Element Status Notes
auth() TPEN.attachAuthentication(this) in connectedCallback
connectedCallback() Present
tpen-project-loaded TPEN.eventDispatcher.on("tpen-project-loaded", () => this.render())
CheckPermissions No permission checks
render() Present
Event listeners ⚠️ Uses addEventListeners() - inconsistent naming
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

9. NavigationManager (components/navigation-manager/index.js)

Element Status Notes
auth() TPEN.attachAuthentication(this) in connectedCallback
connectedCallback() Present
tpen-project-loaded TPEN.eventDispatcher.on("tpen-project-loaded", () => this.render())
CheckPermissions No permission checks
render() Present
Event listeners ⚠️ Uses setupEventListeners() - inconsistent naming
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

10. ContinueWorking (components/continue-working/index.js)

Element Status Notes
auth() TPEN.attachAuthentication(this) in connectedCallback
connectedCallback() Present
tpen-project-loaded No listener - renders immediately
CheckPermissions No permission checks
render() Present
Event listeners ⚠️ Uses setupEventListeners()
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

11. CreateColumn (components/create-column/index.js)

Element Status Notes
auth() TPEN.attachAuthentication(this) in connectedCallback
connectedCallback() Present
tpen-project-loaded TPEN.eventDispatcher.on("tpen-project-loaded", (ev) => this.render())
CheckPermissions ⚠️ Uses CheckPermissions.checkAllAccess but not in standard pattern
render() Present
Event listeners ⚠️ Uses setupEventListeners()
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

12. ProjectMetadata (components/project-metadata/index.js)

Element Status Notes
auth() TPEN.attachAuthentication(this) in connectedCallback
connectedCallback() Present
tpen-project-loaded TPEN.eventDispatcher.on("tpen-project-loaded", () => this.render())
CheckPermissions Uses CheckPermissions.checkViewAccess('PROJECT', 'METADATA')
render() Present
Event listeners ⚠️ Inline registration
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

13. ProjectPermissions (components/project-permissions/index.js)

Element Status Notes
auth() TPEN.attachAuthentication(this) in connectedCallback
connectedCallback() Present
tpen-project-loaded TPEN.eventDispatcher.on("tpen-project-loaded", () => this.render())
CheckPermissions Uses CheckPermissions.checkEditAccess('PROJECT') for edit operations
render() Present
Event listeners ⚠️ Inline registration
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

14. ProjectLayers (components/project-layers/index.js)

Element Status Notes
auth() TPEN.attachAuthentication(this) in connectedCallback
connectedCallback() Present
tpen-project-loaded TPEN.eventDispatcher.on("tpen-project-loaded", () => this.render())
CheckPermissions Uses CheckPermissions.checkViewAccess('PROJECT', 'LAYERS')
render() Present
Event listeners ⚠️ Inline registration
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

15. ProjectDetails (components/project-details/index.js)

Element Status Notes
auth() TPEN.attachAuthentication(this) in connectedCallback
connectedCallback() Present
tpen-project-loaded TPEN.eventDispatcher.on("tpen-project-loaded", () => this.render())
CheckPermissions Uses CheckPermissions.checkViewAccess('PROJECT')
render() Present
Event listeners ⚠️ Uses setupEventListeners()
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

16. ProjectOptions (components/project-options/index.js)

Element Status Notes
auth() TPEN.attachAuthentication(this) in connectedCallback
connectedCallback() Present
tpen-project-loaded TPEN.eventDispatcher.on("tpen-project-loaded", () => this.render())
CheckPermissions Uses CheckPermissions.checkEditAccess('PROJECT')
render() Present
Event listeners ⚠️ Uses addEventListeners()
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

17. ProjectExport (components/project-export/index.js)

Element Status Notes
auth() TPEN.attachAuthentication(this) in connectedCallback
connectedCallback() Present
tpen-project-loaded TPEN.eventDispatcher.on("tpen-project-loaded", () => this.render())
CheckPermissions Uses CheckPermissions.checkViewAccess('PROJECT')
render() Present
Event listeners ⚠️ Inline registration
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

18. ProjectTools (components/project-tools/index.js)

Element Status Notes
auth() TPEN.attachAuthentication(this) in connectedCallback
connectedCallback() Present
tpen-project-loaded TPEN.eventDispatcher.on("tpen-project-loaded", () => this.render())
CheckPermissions Uses CheckPermissions.checkViewAccess('PROJECT', 'TOOLS')
render() Present
Event listeners ⚠️ Inline registration
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

19-22. Copy Project Variants (copy-existing-project, copy-project-with-customization, copy-project-with-group-member, copy-project-without-annotations)

All share similar patterns:

Element Status Notes
auth() Present
connectedCallback() Present
tpen-project-loaded Present
CheckPermissions Missing
render() Present
Event listeners ⚠️ Inline registration
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

23. ImportImage (components/import-image/index.js)

Element Status Notes
auth() TPEN.attachAuthentication(this) in connectedCallback
connectedCallback() Present
tpen-project-loaded Not needed - standalone form
CheckPermissions Not needed - creates new project
render() Present
Event listeners ⚠️ Uses setupEventListeners()
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

24. ImportProject (components/import-project/index.js)

Element Status Notes
auth() TPEN.attachAuthentication(this) in connectedCallback
connectedCallback() Present
tpen-project-loaded Not needed - standalone form
CheckPermissions Not needed - creates new project
render() Present
Event listeners ⚠️ Uses setupEventListeners()
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

25. ManageLayers (components/manage-layers/index.js)

Element Status Notes
auth() TPEN.attachAuthentication(this) in connectedCallback
connectedCallback() Present
tpen-project-loaded TPEN.eventDispatcher.on("tpen-project-loaded", () => this.render())
CheckPermissions Uses CheckPermissions.checkEditAccess('PROJECT', 'LAYERS')
render() Present
Event listeners ⚠️ Uses addEventListeners()
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

26. ManagePages (components/manage-pages/index.js)

Element Status Notes
auth() TPEN.attachAuthentication(this) in connectedCallback
connectedCallback() Present
tpen-project-loaded TPEN.eventDispatcher.on("tpen-project-loaded", () => this.render())
CheckPermissions Uses CheckPermissions.checkEditAccess('PROJECT', 'PAGES')
render() Present
Event listeners ⚠️ Uses setupEventListeners()
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

27. LayerSelector (components/layer-selector/index.js)

Element Status Notes
auth() TPEN.attachAuthentication(this) in connectedCallback
connectedCallback() Present
tpen-project-loaded Present
CheckPermissions No permission checks
render() Present
Event listeners ⚠️ Inline registration
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

28. ColumnSelector (components/column-selector/index.js)

Element Status Notes
auth() TPEN.attachAuthentication(this) in connectedCallback
connectedCallback() Present
tpen-project-loaded Present
CheckPermissions No permission checks
render() Present
Event listeners ⚠️ Inline registration
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

29. PageTool (components/page-tool/index.js)

Element Status Notes
auth() TPEN.attachAuthentication(this) in connectedCallback
connectedCallback() Present
tpen-project-loaded Present
CheckPermissions No permission checks
render() Present
Event listeners ⚠️ Uses setupEventListeners()
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

30. MagnifierTool (components/magnifier-tool/index.js)

Element Status Notes
auth() No auth attachment
connectedCallback() Present
tpen-project-loaded Not used
CheckPermissions No permission checks
render() Present
Event listeners ⚠️ Inline registration
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

31. QuicktypeManager (components/quicktype-manager/index.js)

Element Status Notes
auth() TPEN.attachAuthentication(this) in connectedCallback
connectedCallback() Present
tpen-project-loaded TPEN.eventDispatcher.on("tpen-project-loaded", () => this.render())
CheckPermissions Uses CheckPermissions.checkEditAccess('PROJECT', 'OPTIONS')
render() Present
Event listeners ⚠️ Uses setupEventListeners()
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

32. QuicktypeTool (components/quicktype-tool/index.js)

Element Status Notes
auth() TPEN.attachAuthentication(this) in connectedCallback
connectedCallback() Present
tpen-project-loaded Present
CheckPermissions No permission checks
render() Present
Event listeners ⚠️ Uses setupEventListeners()
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

33. QuicktypeEditorDialog (components/quicktype-tool/quicktype-editor-dialog.js)

Element Status Notes
auth() No auth attachment
connectedCallback() Present - just calls render()
tpen-project-loaded Not used - dialog component
CheckPermissions No permission checks
render() Present
Event listeners ⚠️ Uses setupEventListeners() - called on open()
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK - lots of event listeners

34. WorkspaceTools (components/workspace-tools/index.js)

Element Status Notes
auth() TPEN.attachAuthentication(this) in connectedCallback
connectedCallback() Present
tpen-project-loaded Present
CheckPermissions No permission checks
render() Present
Event listeners ⚠️ Inline registration
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

35. SplitscreenTool (components/splitscreen-tool/index.js)

Element Status Notes
auth() TPEN.attachAuthentication(this) in connectedCallback
connectedCallback() Present
tpen-project-loaded Present
CheckPermissions No permission checks
render() Present
Event listeners ⚠️ Inline registration
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

36. TranscriptionBlock (components/transcription-block/index.js)

Element Status Notes
auth() TPEN.attachAuthentication(this) in connectedCallback
connectedCallback() Present
tpen-project-loaded Present
CheckPermissions No permission checks
render() Present
Event listeners ⚠️ Uses addEventListeners()
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

37. SimpleTranscription (components/simple-transcription/index.js)

Element Status Notes
auth() TPEN.attachAuthentication(this) in connectedCallback
connectedCallback() Present
tpen-project-loaded TPEN.eventDispatcher.on("tpen-project-loaded", () => this.render())
CheckPermissions Uses CheckPermissions.checkViewAccess('LINE', 'TEXT')
render() Present
Event listeners ⚠️ Uses setupEventListeners()
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

38. UpdateMetadata (components/update-metadata/index.js)

Element Status Notes
auth() TPEN.attachAuthentication(this) in connectedCallback
connectedCallback() Present
tpen-project-loaded eventDispatcher.on("tpen-project-loaded", () => this.openModal())
CheckPermissions Uses CheckPermissions.checkViewAccess('PROJECT', 'METADATA') and checkEditAccess
render() ⚠️ No explicit render() - uses openModal()
Event listeners Uses setupEventListeners()
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

39. RolesHandler (components/roles-handler/index.js)

Element Status Notes
auth() TPEN.attachAuthentication(this) in connectedCallback
connectedCallback() Present
tpen-project-loaded eventDispatcher.on('tpen-project-loaded', () => this.render())
CheckPermissions No permission checks
render() Present
Event listeners ⚠️ Uses registerEventListeners()
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

40. QuickGuide (components/quick-guide/index.js)

Element Status Notes
auth() No auth attachment (may be intentional - public component)
connectedCallback() Present
tpen-project-loaded Not used
CheckPermissions Not needed
render() Present
Event listeners ⚠️ Inline registration
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

41. SplitScreen (components/split-screen/index.js)

Element Status Notes
auth() No auth attachment
connectedCallback() Present
tpen-project-loaded Not used
CheckPermissions Not needed - UI layout component
render() Present
Event listeners ⚠️ Inline registration with resize events
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK - has window resize listener

42-43. Annotorious Components (plain.js, line-parser.js)

Element Status Notes
auth() TPEN.attachAuthentication(this) in constructor
connectedCallback() Present
tpen-project-loaded Uses in line-parser.js
CheckPermissions Uses CheckPermissions.checkAllAccess("LINE", "SELECTOR") in line-parser
render() Present (calls loadAnnotorious)
Event listeners ⚠️ Uses listenTo() method
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK - has window beforeunload listener

44. LegacyAnnotator (components/legacy-annotator/plain.js)

Element Status Notes
auth() TPEN.attachAuthentication(this) in constructor
connectedCallback() Present
tpen-project-loaded Not used
CheckPermissions No permission checks
render() ⚠️ No explicit render() - uses processAnnotationPage
Event listeners ⚠️ Uses listen() method
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

45. TpenQuickType (components/quicktype/quicktype.js)

Element Status Notes
auth() TPEN.attachAuthentication(this) in constructor
connectedCallback() Present
tpen-project-loaded In constructor: eventDispatcher.on("tpen-project-loaded", () => this.loadQuickType())
CheckPermissions No permission checks
render() Present
Event listeners Uses setupEventListeners()
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

46. NewAction (components/new-action.js)

Element Status Notes
auth() No auth attachment
connectedCallback() Not present - uses constructor only
tpen-project-loaded Not needed
CheckPermissions Not needed - navigation component
render() ⚠️ Inline in constructor
Event listeners ⚠️ Inline in constructor
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

Category 2: GUI Components (Utility/Display)

These are UI utility components that may not need the full lifecycle pattern.

47. Alert (components/gui/alert/Alert.js)

Element Status Notes
auth() N/A Not needed - UI component
connectedCallback() Present
tpen-project-loaded N/A Not needed
CheckPermissions N/A Not needed
render() Present
Event listeners ⚠️ Inline registration
disconnectedCallback() 🔶❌ MISSING

48. AlertContainer (components/gui/alert/AlertContainer.js)

Element Status Notes
auth() N/A Not needed
connectedCallback() Present
tpen-project-loaded N/A Not needed
CheckPermissions N/A Not needed
render() ⚠️ Inline in connectedCallback
Event listeners ⚠️ Uses TPEN.eventDispatcher.on()
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

49. Confirm (components/gui/confirm/Confirm.js)

Element Status Notes
auth() N/A Not needed
connectedCallback() Present
tpen-project-loaded N/A Not needed
CheckPermissions N/A Not needed
render() Present
Event listeners ⚠️ Inline registration
disconnectedCallback() 🔶❌ MISSING

50. ConfirmContainer (components/gui/confirm/ConfirmContainer.js)

Element Status Notes
auth() N/A Not needed
connectedCallback() Present
tpen-project-loaded N/A Not needed
CheckPermissions N/A Not needed
render() ⚠️ Inline in connectedCallback
Event listeners ⚠️ Uses TPEN.eventDispatcher.on()
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

51. Toast (components/gui/toast/Toast.js)

Element Status Notes
auth() N/A Not needed
connectedCallback() Present
tpen-project-loaded N/A Not needed
CheckPermissions N/A Not needed
render() Present
Event listeners ⚠️ Inline registration
disconnectedCallback() 🔶❌ MISSING

52. ToastContainer (components/gui/toast/ToastContainer.js)

Element Status Notes
auth() N/A Not needed
connectedCallback() Present
tpen-project-loaded N/A Not needed
CheckPermissions N/A Not needed
render() ⚠️ Inline in connectedCallback
Event listeners ⚠️ Uses TPEN.eventDispatcher.on()
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

53. Header (components/gui/site/Header.js)

Element Status Notes
auth() TPEN.attachAuthentication(this)
connectedCallback() Present
tpen-project-loaded N/A Not needed
CheckPermissions N/A Not needed
render() Present
Event listeners ⚠️ Uses addEventListeners()
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

54. NoAuthHeader (components/gui/site/NoAuthHeader.js)

Element Status Notes
auth() N/A Not needed - no auth header
connectedCallback() Present
tpen-project-loaded N/A Not needed
CheckPermissions N/A Not needed
render() Present
Event listeners ⚠️ Inline registration
disconnectedCallback() Missing

55. Page (components/gui/site/Page.js)

Element Status Notes
auth() Uses authgate() function
connectedCallback() Present
tpen-project-loaded N/A Not needed
CheckPermissions N/A Not needed
render() Present
Event listeners ⚠️ Uses addEventListener()
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

56. Footer (components/gui/site/Footer.js)

Element Status Notes
auth() N/A Not needed
connectedCallback() Present
tpen-project-loaded N/A Not needed
CheckPermissions N/A Not needed
render() Present
Event listeners N/A None needed
disconnectedCallback() Not needed - no listeners

57. Card (components/gui/card/Card.js)

Element Status Notes
auth() N/A Not needed
connectedCallback() Present
tpen-project-loaded N/A Not needed
CheckPermissions N/A Not needed
render() ⚠️ Inline in connectedCallback
Event listeners N/A None
disconnectedCallback() Not needed - no listeners

58-59. FixedExplanatoryGuide, RelativeExplanatoryGuide

Element Status Notes
auth() N/A Not needed
connectedCallback() Present
tpen-project-loaded N/A Not needed
CheckPermissions N/A Not needed
render() Present
Event listeners ⚠️ Inline registration
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

Category 3: Project List Components

60. ProjectList (components/projects/index.js)

Element Status Notes
auth() TPEN.attachAuthentication(this) in connectedCallback
connectedCallback() Present
tpen-project-loaded Not used - different event pattern
CheckPermissions N/A Lists user's projects
render() Present
Event listeners ⚠️ Uses addEventListeners()
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

61. ProjectListView (components/projects/project-list-view.js)

Element Status Notes
auth() TPEN.attachAuthentication(this) in connectedCallback
connectedCallback() Present
tpen-project-loaded Not used
CheckPermissions N/A View component
render() Present
Event listeners ⚠️ Inline registration
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

62. ProjectListWrite (components/projects/project-list-write.js)

Element Status Notes
auth() TPEN.attachAuthentication(this) in connectedCallback
connectedCallback() Present
tpen-project-loaded Not used
CheckPermissions N/A View component
render() Present
Event listeners ⚠️ Uses setupEventListeners()
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

63. ProjectHeader (components/projects/project-header.js)

Element Status Notes
auth() No auth attachment
connectedCallback() Present
tpen-project-loaded Not used
CheckPermissions N/A View component
render() Present
Event listeners N/A None
disconnectedCallback() Not needed

64. ListNavigation (components/projects/list-navigation.js)

Element Status Notes
auth() No auth attachment
connectedCallback() Present
tpen-project-loaded Not used
CheckPermissions N/A Navigation component
render() Present
Event listeners ⚠️ Uses addEventListeners()
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

Category 4: User/Auth Components

65. PublicLogin (components/public-login/index.js)

Element Status Notes
auth() N/A Login component
connectedCallback() Present
tpen-project-loaded N/A Not needed
CheckPermissions N/A Not needed
render() Present
Event listeners ⚠️ Inline registration
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

66. PublicUserProfile (components/public-user-profile/index.js)

Element Status Notes
auth() No auth - public profile
connectedCallback() Present
tpen-project-loaded N/A Not needed
CheckPermissions N/A Public component
render() Present
Event listeners ⚠️ Inline registration
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

67. UserProfile (components/user-profile/index.js)

Element Status Notes
auth() TPEN.attachAuthentication(this)
connectedCallback() Present
tpen-project-loaded N/A Not needed
CheckPermissions N/A User's own profile
render() Present
Event listeners ⚠️ Uses setupEventListeners()
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

68-70. User Profile Sub-components (ContributionActivity, Report, UserStats)

Element Status Notes
auth() Via parent or direct
connectedCallback() Present
tpen-project-loaded N/A Not needed
CheckPermissions N/A User's own data
render() Present
Event listeners ⚠️ Inline registration
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

Category 5: Interface Entry Points

71. TranscriptionInterface (interfaces/transcription/index.js)

Element Status Notes
auth() TPEN.attachAuthentication(document.body)
connectedCallback() N/A Not a web component - entry script
tpen-project-loaded Uses event listener
CheckPermissions Uses CheckPermissions.checkViewAccess and checkEditAccess
render() Present
Event listeners ⚠️ Multiple event listeners
disconnectedCallback() N/A Not a web component

72. ManageColumnsInterface (interfaces/manage-columns/index.js)

Element Status Notes
auth() Present
connectedCallback() N/A Entry script
tpen-project-loaded Present
CheckPermissions Uses CheckPermissions
render() Present
Event listeners ⚠️ Multiple
disconnectedCallback() N/A Entry script

73. ManageProjectInterface (interfaces/manage-project/index.js)

Element Status Notes
auth() TPEN.attachAuthentication(container)
connectedCallback() N/A Entry script
tpen-project-loaded TPEN.eventDispatcher.on('tpen-project-loaded', () => render())
CheckPermissions Uses CheckPermissions.checkEditAccess('PROJECT')
render() Present
Event listeners ⚠️ Multiple
disconnectedCallback() N/A Entry script

74. TpenCustomProperty (interfaces/custom/index.js)

Element Status Notes
auth() TPEN.attachAuthentication(this) in connectedCallback
connectedCallback() Present
tpen-project-loaded TPEN.eventDispatcher.on('tpen-project-loaded', () => this.render(), this)
CheckPermissions No permission checks
render() Present
Event listeners ⚠️ Inline registration
disconnectedCallback() 🔶❌ MISSING - MEMORY LEAK RISK

75. ImportTpen28Interface (interfaces/import-tpen28/index.js)

Not a web component - entry script with event listeners attached to DOM elements.

76. NavigationInterface (interfaces/navigation/index.js)

Just imports NavigationManager component.

77. QuicktypeInterface (interfaces/quicktype/index.js)

Just imports QuicktypeManager component.


Category 6: Check Permissions Components

78. CheckPermissions (components/check-permissions/checkPermissions.js)

Static utility class - not a web component.

79. PermissionMatchElement (components/check-permissions/permission-match-element.js)

Element Status Notes
auth() N/A Uses parent permissions
connectedCallback() Present
tpen-project-loaded Listens for project changes
CheckPermissions Uses CheckPermissions internally
render() updateVisibility() acts as render
Event listeners Global event listener setup
disconnectedCallback() PROPERLY IMPLEMENTED - removes event listener

80. PermissionMatch (components/check-permissions/permission-match.js)

Element Status Notes
auth() N/A Uses parent permissions
connectedCallback() Present
tpen-project-loaded Listens for project changes
CheckPermissions Uses CheckPermissions internally
render() checkAndApply() acts as render
Event listeners Global event listener setup
disconnectedCallback() PROPERLY IMPLEMENTED - removes event listener

Category 7: Utility/Non-Component Files

The following files are NOT web components:

  • components/iiif-tools/index.js - Utility functions
  • components/annotorious-annotator/detect-lines.js - Utility functions
  • components/quicktype/validation.js - Validation functions

Summary Statistics

Components with disconnectedCallback() Implemented

Only 3 components properly implement disconnectedCallback():

  1. PermissionMatchElement - removes event listener
  2. PermissionMatch - removes event listener
  3. Footer (not needed - no listeners)
  4. Card (not needed - no listeners)
  5. ProjectHeader (not needed - no listeners)

Components Missing disconnectedCallback() (Memory Leak Risk)

~65+ components are missing disconnectedCallback() and register event listeners that are never cleaned up.

Event Listener Method Naming Inconsistencies

Method Name Count Components Using
setupEventListeners() ~15 NavigationManager, ManagePages, PageTool, QuicktypeTool, etc.
addEventListeners() ~10 LeaveProject, ManageLayers, TranscriptionBlock, Header, etc.
attachEventListeners() ~5 DefaultTranscribe, LineText, etc.
registerEventListeners() ~3 ManageRole, RolesHandler
listen() ~2 LegacyAnnotator
listenTo() ~2 AnnotoriousAnnotator variants
Inline registration ~40+ Most components

CheckPermissions Usage

Status Count Notes
✅ Uses CheckPermissions ~20 Properly gating render
❌ Missing CheckPermissions ~45+ Should be audited for need

Auth Pattern Usage

Pattern Count Notes
TPEN.attachAuthentication(this) in connectedCallback ~40 Most common
TPEN.attachAuthentication(this) in constructor ~5 Annotorious variants
authgate() function ~2 Page component
No auth ~20 May be intentional for public components

Recommendations

1. Standardize Event Listener Method Names

Choose one name and use it consistently:

// Recommended: setupEventListeners()
setupEventListeners() {
    // All event listener registration here
}

2. Implement disconnectedCallback() for All Components

Every component that registers event listeners should clean them up:

disconnectedCallback() {
    // Remove all event listeners
    TPEN.eventDispatcher.off('tpen-project-loaded', this._boundRender)
    // Remove DOM event listeners
    this.someElement?.removeEventListener('click', this._boundHandler)
}

3. Store Bound Event Handlers

To properly remove event listeners, store bound handlers:

connectedCallback() {
    this._boundRender = this.render.bind(this)
    TPEN.eventDispatcher.on('tpen-project-loaded', this._boundRender)
}

disconnectedCallback() {
    TPEN.eventDispatcher.off('tpen-project-loaded', this._boundRender)
}

4. Standardize Auth Pattern

Implement authgate() consistently:

async authgate() {
    TPEN.attachAuthentication(this)
    if (!TPEN.getAuthorization()) {
        // Handle unauthenticated state
    }
}

5. Implement Permission Checks Consistently

All project-dependent components should check permissions before rendering:

render() {
    if (!CheckPermissions.checkViewAccess('RESOURCE_TYPE')) {
        this.shadowRoot.innerHTML = '<p>Access denied</p>'
        return
    }
    // Normal render logic
}

6. Create a Base Component Class

Consider creating a base class that implements the standard lifecycle:

class TpenComponent extends HTMLElement {
    constructor() {
        super()
        this.attachShadow({ mode: 'open' })
        this._eventListeners = []
    }

    connectedCallback() {
        TPEN.attachAuthentication(this)
        this._boundProjectLoaded = this.onProjectLoaded.bind(this)
        TPEN.eventDispatcher.on('tpen-project-loaded', this._boundProjectLoaded)
    }

    disconnectedCallback() {
        TPEN.eventDispatcher.off('tpen-project-loaded', this._boundProjectLoaded)
        this._eventListeners.forEach(({ element, event, handler }) => {
            element.removeEventListener(event, handler)
        })
    }

    onProjectLoaded() {
        this.checkPermissions() && this.render()
    }

    checkPermissions() {
        return true // Override in subclass
    }

    render() {
        // Override in subclass
    }

    addListener(element, event, handler) {
        element.addEventListener(event, handler)
        this._eventListeners.push({ element, event, handler })
    }
}

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions