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

Started adding JSDoc #984

Closed
wants to merge 1 commit into from
Closed
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
87 changes: 87 additions & 0 deletions src/core/session.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ import { PageView } from "./drive/page_view"
import { FrameElement } from "../elements/frame_element"
import { Preloader } from "./drive/preloader"

/**
* The Session class represents a user session, managing the various observers, navigators,
* and controllers to provide a seamless user experience in a Turbo Drive enabled application.
*/
export class Session {
navigator = new Navigator(this)
history = new History(this)
Expand All @@ -40,6 +44,9 @@ export class Session {
started = false
formMode = "on"

/**
* Starts all observers and services if not already started.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about adding a return here?

* @returns {void}

*/
start() {
if (!this.started) {
this.pageObserver.start()
Expand All @@ -57,10 +64,16 @@ export class Session {
}
}

/**
* Disables the session.
*/
disable() {
this.enabled = false
}

/**
* Stops all observers and services if they are running.
*/
stop() {
if (this.started) {
this.pageObserver.stop()
Expand All @@ -76,10 +89,22 @@ export class Session {
}
}

/**
* Registers a new browser adapter for the session.
*
* @param {BrowserAdapter} adapter - The new browser adapter to register.
*/
registerAdapter(adapter) {
this.adapter = adapter
}

/**
* Initiates a visit to a specified location, with optional parameters.
*
* @param {string} location - The location to visit.
* @param {Object} options - Optional parameters for the visit.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to define the options properties? This can be done via @typedef.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since options is optional, you can use square brackets around the description

* @param {Object} [options] - Optional parameters for the visit.

* @param {string} [options.frame] - The ID of the frame element to use for the visit.
*/
visit(location, options = {}) {
const frameElement = options.frame ? document.getElementById(options.frame) : null

Expand All @@ -91,26 +116,54 @@ export class Session {
}
}

/**
* Connects a new stream source.
*
* @param {any} source - The new stream source to connect.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is either EventSource | WebSocket

*/
connectStreamSource(source) {
this.streamObserver.connectStreamSource(source)
}

/**
* Disconnects an existing stream source.
*
* @param {any} source - The stream source to disconnect.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is either EventSource | WebSocket

*/
disconnectStreamSource(source) {
this.streamObserver.disconnectStreamSource(source)
}

/**
* Renders a stream message.
*
* @param {Object} message - The stream message to render.
*/
renderStreamMessage(message) {
this.streamMessageRenderer.render(StreamMessage.wrap(message))
}

/**
* Clears the cache.
*/
clearCache() {
this.view.clearSnapshotCache()
}

/**
* Sets the delay for the progress bar.
*
* @param {number} delay - The delay in milliseconds.
*/
setProgressBarDelay(delay) {
this.progressBarDelay = delay
}

/**
* Sets the form mode.
*
* @param {string} mode - The form mode to set, either "on" or "off".
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be typed as @param {('on'|'off')} mode.

*/
setFormMode(mode) {
this.formMode = mode
}
Expand Down Expand Up @@ -361,6 +414,14 @@ export class Session {

// Helpers

/**
* Helper function to check if a submission is navigatable based on the current form mode and element attributes.
*
* @private
* @param {HTMLFormElement} form - The form element being submitted.
* @param {HTMLElement} submitter - The element that triggered the form submission.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

submitter is optional, it should therefore be typed as @param {HTMLElement} [submitter].

* @return {boolean} - True if the submission is navigatable, false otherwise.
*/
submissionIsNavigatable(form, submitter) {
if (this.formMode == "off") {
return false
Expand All @@ -375,6 +436,13 @@ export class Session {
}
}

/**
* Helper function to check if an element is navigatable based on data attributes and the current drive mode.
*
* @private
* @param {HTMLElement} element - The element to check.
* @return {boolean} - True if the element is navigatable, false otherwise.
*/
elementIsNavigatable(element) {
const container = findClosestRecursively(element, "[data-turbo]")
const withinFrame = findClosestRecursively(element, "turbo-frame")
Expand All @@ -399,10 +467,24 @@ export class Session {

// Private

/**
* Helper function to get the appropriate action for a link click.
*
* @private
* @param {HTMLAnchorElement} link - The link being clicked.
* @return {string} - The action to perform for the link click.
*/
getActionForLink(link) {
return getVisitAction(link) || "advance"
}

/**
* Helper function to get the current page snapshot.
*
* @private
* @readonly
* @return {Snapshot} - The current page snapshot.
*/
get snapshot() {
return this.view.snapshot
}
Expand All @@ -419,6 +501,11 @@ export class Session {
// older adapters which do not expect URL objects. We should
// consider removing this support at some point in the future.

/**
* Extends a URL with deprecated properties for compatibility with older Turbo Native adapters.
*
* @param {URL} url - The URL to extend.
*/
function extendURLWithDeprecatedProperties(url) {
Object.defineProperties(url, deprecatedLocationPropertyDescriptors)
}
Expand Down
70 changes: 70 additions & 0 deletions src/core/snapshot.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,68 @@
/**
* Represents a snapshot of an HTML element along with various properties and methods to interact with it.
*/
export class Snapshot {
/**
* Creates a new snapshot instance.
*
* @param {Element} element - The HTML element to create a snapshot of.
*/
constructor(element) {
this.element = element
}

/**
* Gets the active element in the document that owns the snapshot element.
*
* @returns {Element | null} - The active element in the document, or null if there is no active element.
*/
get activeElement() {
return this.element.ownerDocument.activeElement
}

/**
* Gets an array of the child elements of the snapshot element.
*
* @returns {Element[]} - An array of the child elements.
*/
get children() {
return [...this.element.children]
}

/**
* Checks if the snapshot element contains a specific anchor.
*
* @param {string} anchor - The anchor to check for.
* @returns {boolean} - True if the anchor exists in the snapshot element, false otherwise.
*/
hasAnchor(anchor) {
return this.getElementForAnchor(anchor) != null
}

/**
* Gets an element associated with a specific anchor in the snapshot element.
*
* @param {string} anchor - The anchor to get the element for.
* @returns {Element | null} - The element associated with the anchor, or null if no such element exists.
*/
getElementForAnchor(anchor) {
return anchor ? this.element.querySelector(`[id='${anchor}'], a[name='${anchor}']`) : null
}

/**
* Checks if the snapshot element is connected to the document.
*
* @returns {boolean} - True if the snapshot element is connected to the document, false otherwise.
*/
get isConnected() {
return this.element.isConnected
}

/**
* Gets the first element in the snapshot that can be auto-focused, if any.
*
* @returns {Element | null} - The first auto-focusable element, or null if no such element exists.
*/
get firstAutofocusableElement() {
const inertDisabledOrHidden = "[inert], :disabled, [hidden], details:not([open]), dialog:not([open])"

Expand All @@ -34,14 +74,31 @@ export class Snapshot {
return null
}

/**
* Gets all permanent elements in the snapshot.
*
* @returns {NodeListOf<Element>} - A node list of all permanent elements in the snapshot.
*/
get permanentElements() {
return queryPermanentElementsAll(this.element)
}

/**
* Gets a permanent element by its ID.
*
* @param {string} id - The ID of the permanent element to get.
* @returns {Element | null} - The permanent element with the specified ID, or null if no such element exists.
*/
getPermanentElementById(id) {
return getPermanentElementById(this.element, id)
}

/**
* Creates a map of permanent elements for a given snapshot.
*
* @param {Snapshot} snapshot - The snapshot to create the permanent element map for.
* @returns {Object} - A map of permanent elements.
*/
getPermanentElementMapForSnapshot(snapshot) {
const permanentElementMap = {}

Expand All @@ -57,10 +114,23 @@ export class Snapshot {
}
}

/**
* Gets a permanent element by its ID within a specific node.
*
* @param {Node} node - The node to query for the permanent element.
* @param {string} id - The ID of the permanent element to get.
* @returns {Element | null} - The permanent element with the specified ID, or null if no such element exists.
*/
export function getPermanentElementById(node, id) {
return node.querySelector(`#${id}[data-turbo-permanent]`)
}

/**
* Queries all permanent elements within a specific node.
*
* @param {Node} node - The node to query for permanent elements.
* @returns {NodeListOf<Element>} - A node list of all permanent elements in the node.
*/
export function queryPermanentElementsAll(node) {
return node.querySelectorAll("[id][data-turbo-permanent]")
}
Loading