Reference

You are browsing documentation for an upcoming version of PageDriver. Documentation and features of this release are subject to change.

Can't find what you're looking for?

If you find yourself going to 3rd parties for answers (such as StackOverflow), please search our documentation issue list first, and submit your question if one doesn't already exist.

BIND

The backbone of unobnoxious extendability.

  • BIND should have as few external dependencies as possible to allow ultimate flexibility.
  • Originally was a 'mixin' with foo.add('action',()=>console.log('bar')) style code, but it was refactored with the rationale of reduced duplication/memory consumption, and the ability to arrange bindings for dynamically added bind interfaces.

Any object may have a bind queue, but only the object itself should execute them. Currently we don't prevent remote bind execution, however prevention may be enforced in the future.

Binds allow the creation of interface-like structures.

Typedef

BindFilter function

Used to filter bound handlers for execution by bindExec.

Returns: boolean β‡’ True when at least one exact matching trigger is found.

MUST return a boolean for success or failure.

ParameterTypeDescription
triggersstring

Used by the filter function receiving the current bind ID iteration.

argsobject

Arguments passed to bound function.

iteratornumber

Current location in the binds array index. Utilize in the following way: this.list[iterator].triggers

bindInterface.mjs

bindInterface object

A single interface within the bind registry.

PropertyTypeDescription
[mustBeNamed]boolean

DEFAULT TRUE. When true, causes a type error to be thrown when an anonymous bind function is added.

[add]function

Additional processing when adding a binding. Can abort the process... by throwing an error :P

[disable]function

Additional processing when disabling bindings. Bindings are never deleted, only disabled - usually by setting runsRemaining to -1.

filterBindFilter
[execOnAdd]object

(EXPERIMENT) Simulated execution args used during initial binding when execOnAdd is true. See BIND/bind options.to add/remove binds. Does it make more sense to allow unique simulated args on a per-bindExec basis instead? Feedback welcome.

listArray.<Binding>
namestring

Self-reflection, primarily for debugging.

addedAtDOMHighResTimeStamp
[boundTo]bindTarget

(CONCEPT) Would permanently bind the interface to whatever calls bindExec() first and deny execution anywhere else. This would be be non-trivial as it would likely rely on some sort of container object reflection.

[boundAt]DOMHighResTimeStamp

(CONCEPT) See above.

[scopes]Object.<string, Array.<number>>

(CONCEPT) for lookups? Could use code similar to EVENT add/disable bindings. Currently there is experimental scoping at the per-bind level.

Example

// Sample containing a single binding
SAMPLE_INTERFACE:{
	name:'SAMPLE_INTERFACE',
	boundTo:null,
	mustBeNamed:true,
	boundAt:null,
	createdAt:79.29999,
	scopes:{
		'#sampleID':[0],
		'.sampleClass':[0],
	},
	list:[{
		id:0,
		runsRemaining:Infinity,
		scope:['#sample','.sample'],
		triggers:['click'],
		handler:()=>{ console.log('hello world'); },
		_STATS:{
			runs:[123],
			added:performance.now(),
			disabled:null,
		},
	}],
};
bindInterface.mjs

bindRegistry Object.<string, bindInterface>

Registry contains all potential project bindings, grouped by interface name.

bind_registry.mjs

Binding object

A single binding within a bindInterface potentially executed by BIND.bindExec.

was thinking of {Array.<CSSStyleRule.selectorText>} scopes (EXPERIMENTAL) for lookups? Could use code similar to EVENT add/disable bindings

PropertyTypeDescription
triggersArray.<string>

Array of strings. Used by bind execution filter.

[scope]string | Array.<string>

(EXPERIMENTAL) secondary filtering layer, primarily intended for DOM based filtering, but who knows...

handlerfunction

executed when triggers passes the BIND/bindExec filter. It is scoped to this object (this returns the current binding).

idnumber

array ID, useful for self removal.

runsRemainingnumber

Optional, default is Infinity

statsobject

Ideally moved to an accessible dedicated stats module allowing custom reporting, as adding binds to the binds module seems like a terrible idea.

stats.addedDOMHighResTimeStamp

Currently performance.now. Likely to change. See #165 Date.now() in STATS.

stats.runsArray.<DOMHighResTimeStamp>

Times run (get total via runs.length)

[stats.disabled]DOMHighResTimeStamp

Created when bind is deactivated successfully

bindTarget object | function

Any non-primitive with binds property.

Commonly objects/functions.

PropertyType
boundInterfacebinds
[name]string

Typedef

boundInterface object

PropertyTypeDescription
[list]Array.<Binding>

If this array doesn't exist, it is created on the first successful call to BIND/bind targeting the object.

[mustBeNamed]boolean

When true, causes a TypeError to be thrown when attempting to add an anonymous bind function.

[add]function
[disable]function
[execOnAdd]object

(EXPERIMENTAL) Simulated default details used during initial execution when execOnAdd is true. See BIND/bind options.

Typedef

add function

Optional destination specific processing.

Mixes: BIND.bind

Able to abort bind 'addition' process.

ParameterTypeDescription
triggersArray.<string> | string

Array of strings, or a string which is split by spaces and converted into an array. Used by bind execution filter.

fnfunction

Executed when triggers pass the binds trigger filter

[position]number

(EXPERIMENTAL) Current execution index

disable function

Optional destination specific processing.

Should we have a separate function for disabling by scope?

Mixes: BIND.disableBindByID

Able to abort bind 'disable' process.

ParameterTypeDescription
bindTargetobject
idnumber

bindTarget bind ID #

Class

BindInterface bindInterface β„—

ParameterType
namestring
bindInterface.mjs

DefaultBindObjProps bindTarget β„—

DefaultBindObjProps.mjs

Function

basicFilter() ⊨BindFilterβ‡’ boolean

Default bind filter used by BIND.bindExec unless a custom filter function is provided.

Implements: BindFilter

basicFilter.mjs

bind(target, triggers, handler, [options])β‡’ number

Register a function that executes when the triggers parameter passes the target interface's BIND.bindExec filter.

Returns: number β‡’ Bind ID (execution order)

ParameterTypeDefaultDescription
targetstring

Destination interface name.

triggersArray.<string> | string

Array of strings, or a string which is split by spaces and converted into an array. Used by bind execution filter.

handlerfunction

Executed when triggers pass the bindTarget trigger filter.

[options]object
[options.runsRemaining]numberInfinity

Runs decrease when bound function returns (bool) true. When it reaches 0, the bind is disabled (by ID). (EXPERIMENT) Deactivates the bind function after a set number of successful executions

[options.execOnAdd]boolean

(EXPERIMENT) Executes the function when added, against predefined trigger data. Was added to force mustatio of pre-exiting content.

[options.scope]Array.<string> | string

(EXPERIMENT) secondary execution filter, was originally intended for DOM based filtering.

[options.position]numberlist.length

(EXPERIMENT) Execution index, negative integers are added from the end of the list. If you use this PLEASE let us know your use case! This was added very early on in the development of PageDriver and did not see any use.

bind.mjs

bindAll(scope, target, trigger, binds)β‡’ Array

Most useful in modules with many similar binds.

this predates the split of trigger text (basic filter) & filter function params in the bind system and should be refactored to do so if we continue providing this helper.

Returns: Array β‡’ Bind IDs

ParameterTypeDescription
scopestring
targetbindTarget
triggerstring | function
bindsobject.<function()> | Array.<function(), object>

a function, or an array containing the function and specific bind options

bindAll.mjs

bindExec(target, triggers, args, [meta])β‡’ boolean

Execute bound functions which passed filtration.

Returns: boolean β‡’ True if any bind was executed. This may change in the future to 'successful' calls only, feedback welcome!

ParameterTypeDescription
targetstring

destination bind interface.

triggersstring | Array.<string>

Used by the filter function receiving the current bind ID iteration.

argsobject

arguments passed to bound function

[meta]object

(EXPERIMENT) metadata passed to bound function, currently used by pd.OBSERVE

bindExec.mjs

initBindInterface(target, [options], [registry])β‡’ bindInterface β„—

Load a bind interface, if the target interface doesn't exist it is created.

Stage2 WIP
ParameterTypeDefaultDescription
targetstring

Bind interface name

[options]bindInterface

Merge/replace default bind interface properties.

[registry]bindRegistrydefaultBindRegistry

[EXPERIMENT] Don't change this.

bindInterface.mjs

bindStats(interfaceName, [consoleTable])β‡’ Array

Output may change significantly in future versions as part of stats/analytics module.

Returns: Array β‡’ Triggers, function name, total executions, time added, time disabled.

ParameterTypeDescription
interfaceNamestring
[consoleTable]boolean

additionally output via console.table

bindStats.mjs

disableBindByID(target, id)β‡’ boolean

Deactivate a bind.

Do we replace this with an easier to use public facing scope based function? Does disabling by ID actually feel clunky...? Also, is the boolean return sufficient?

Returns: boolean β‡’ Only returns true when a bind is actually disabled, all other sitautions (such as no bindings, or already being disable return false, see @fyi note.

Optional bindTarget specific bindTarget.boundInterface.disable logic can abort the process.

ParameterType
targetbindTarget
idnumber
disableBindByID.mjs

CONTEXT

Contexts are flat (non-hierarchical) negatable CSS classes - although they may represent sub-states or roles.

Would be interesting if this somehow became scoped... πŸ€”

They are used to toggle common project elements relevant to different personas. For example, elements meant for a regular user, moderator, or manager. See ContextName for naming rules.

PropertyTypeDescription
globalContextStateReservedArray.<ContextName>

Current reserved context names:

  • Javascript - Always active (toggled during INIT). An alternative to <noscript> tags.
  • Debug - Active when DEBUG module debugger is loaded.
  • Kbd - Accessibility (a11y) helper, active when using keyboard-like controls.

Typedef

ContextName string

Context class names:

  • CANNOT begin with a hyphen (-). They are reserved for negated contexts.
  • MUST begin with an uppercase character (ex. 'A' not 'a'. 'Π›' not 'Π»'. 'Đ' not 'Δ‘') to differentiate them from basic CSS classes.
  • CANNOT exist on the {@link CONTEXT.globalContextStateReserved reserved Context list}.

If your programming locale doesn't have capitalization we recommend standardizing a basic latin script prefix (ex. CCログむンする, CC둜그인) across your project.

Valid examples:

  • "Vendor" Basic Latin uppercase
  • "Π›ΠΈΠ΄Π΅Ρ€" Cyrillic uppercase
  • "Đặc_Quyền" Vietnamese uppercase with underscore
  • "CCログむンする" prefixed with uppercase latin script

Invalid examples:

  • "-Vendor" starts with hyphen (use for negation)
  • "vendor" starts lowercased
  • "123User" starts with number, note as of 2025/1/1 this is not enforced programmatically given classes are not normally permitted to begin with numbers.

See CONTEXT.isValidContextName for the runtime validation.

ContextState Object

State of a single Context within the global context container.

PropertyType
stateboolean
addedEpochTimeStamp
[modified]EpochTimeStamp

ContextClass Object.<ContextName, ContextState>

Function

contextInit(contexts) β„—

Context Flag Class initializer, produces necessary CSS.

ParameterType
contextsObject.<ContextName, boolean>
context_init.mjs

isValidContextName(contextName)β‡’ boolean β„—

Ensure potential context names are in proper form (non-negated and begin with a capital character).

ParameterType
contextNamestring
context_isValidContextName.mjs

contextOff(contexts)β‡’ Object.<ContextName, ContextState>

Syntactic sugar for set( ... , false).

Returns: Object.<ContextName, ContextState> β‡’ Returns an empty object when no provided contexts are valid.

ParameterType
contextsArray.<ContextName> | ContextName
context_off.mjs

contextOn(contexts)β‡’ Object.<ContextName, ContextState>

Syntactic sugar for set( ... , true).

Returns: Object.<ContextName, ContextState> β‡’ Returns an empty object when no provided contexts are valid.

ParameterType
contextsArray.<ContextName> | ContextName
context_on.mjs

contextSet(contexts, [state])β‡’ Object.<ContextName, ContextState>

Context Flag base function.

Returns: Object.<ContextName, ContextState> β‡’ Returns an empty object when no provided contexts are valid.

Used by on/off/toggle helpers.

ParameterTypeDescription
contextsArray.<ContextName> | ContextName
[state]undefined | boolean

undefined = toggle, true = on, false = off

context_set.mjs

contextToggle(contexts)β‡’ boolean | Object.<ContextName, ContextState>

Syntactic sugar for set( ... , undefined ), EXCEPT when a SINGLE (1) Context is provided it only returns the boolean state result, allowing for cleaner inlining.

Toggle prevents context function idempotentness. Additional details in CONTEXT.contextSet.

Returns: boolean | Object.<ContextName, ContextState> β‡’ Note this function returns a boolean when only a single Context is provided.

Toggling an undefined Context creates it (in true state).

ParameterTypeDescription
contextsArray.<ContextName> | ContextName

One or more context flags

Example

contextToggle('Foo'); // Returns a boolean
contextToggle(['Foo','Bar']); // Returns { ContextState, ... }
context_toggle.mjs

CLIENTS β„—

Reserved for PageDriver fullstack module

πŸ”’ RESERVED
EditionCORE
Stage1 Discuss
PropertyTypeDescription
sessionStatusIntervalnumber
csrfstring

Cross-Site Request Forgery token.

IPCountrystring

Client's country code in ISO 3166 Alpha-2 format based on IP, if provided by CDN or host. XX when unknown or unavailable.

_META_META
clients.mjs

pd/USER object β„—

πŸ”’ RESERVED
PropertyTypeDescription
otpLengthnumber
avatarArray.<string, string>

URL prefix and suffix

privateobject

Private details

publicobject

Public shareable details

user.mjs

DEBUG

In-browser PageDriver debugger tooling.

EditionCORE

I'm painfully aware how disgusting this code is! Refactoring PRs welcome πŸ’£

  • IIFE?
  • Debug module should retain state internally.
  • 2022/02/14 AFAIK Disabled DEBUG binds are the only remenants after disabling Debug mode.
  • 2022/07/07 [Consistency] use πŸ‘’ unicode arrow in debug output for uniformity

Maybe: VDEBUG.foo.warn('test warning'); Replacing: if (pd.DEBUG && pd.DEBUG.verbose.foo) console.warn('test warning'); HOWEVER: ACTUALLY using .bind would solve this issue... sort of mem leaky tho'... in doing so you lose closure for additional funct, only echoing simpler messages to console... hmm

Adding your own verbose flags is as simple as if (pd.DEBUG && pd.DEBUG.verbose.myCodeFlag) console.log('foo!'), no additional code required. Please be mindful as there are no 'reserved' flags or groups (yet), so ensure both:

  • your output is as specific as possible to a particular flag, or set of flags.
  • the verbose flag property name is specific enough.
PropertyTypeDescription
verboseActivenumber

Total active verbose flags

Function

contextToggle(contextStr, [page])

Class toggler

ParameterTypeDescription
contextStrstring

Roles split by spaces, automatically adds role inverse role.

[page]boolean

'true' toggles at page level instead of body.

debug.mjs

contextVisualDebug()

Toggle visual CSS context debugger - reveals both states

debug.mjs
debug.mjs

debugCookie

stepOpt Otherwise, execute optional `app.notDebugging()` function (to do things such as displaying a default console message/warning)
debug_init.mjs

codeSampleFromDOM β„—

Create code samples from the DOM itself 😎

mutates {HTMLPreElement} binds pd.INIT sideeffectsONLY category DOM taxonomy documentation, code
Stage2 WIP
  1. Ensure source code has an ID attribute
  2. Add <pre data-source="ID_HERE"></pre>, where data-source value matches the source ID.
  3. (optional) add language- class for syntax highlighting
ParameterTypeDescription
sourceIDstring

idk about these tag names...

codeSampleFromDOM.js

markdown bindTarget

Markdown DOM Renderer

category DOM
Stage2 FeedbackNeeded
Marked DOESN'T sanitize output HTML!
ParameterTypeDefaultDescription
elstring | HTMLElement

uses querySelector if not an element.

[content]string

Markdown content

[src]string

Attempts an XHR request for content, overrides content if both are provided

[options]object
[options.headerIds]booleantrue
[options.headerPrefix]booleanmd-
markdown.mjs

MENU

Function

activateMenuItem(req) β„—

ParameterType
reqstring
menu.mjs

updateMenu([containerID])β‡’ boolean

Mutate primary project navigation.

Pages with data-nomenu attribute are ignored. Optional data-icon, data-accesskey page attributes are used if available.

ParameterTypeDefaultDescription
[containerID]stringdynamic

Menu element ID

menu.mjs
menu.mjs

OBSERVER

Project-wide bindable DOM mutation observer.

This module only supports a single active observer for no real reason, although performance and coder sanity are a good excuse as any.

Property
execOnAdd

Function

filter()

Returns false when bind trigger doesn't match an execution trigger.

Returns true when unscoped, otherwise if there is a mutation target:

  • if a childList with added nodes, ensure scope is within target
  • if an attribute, ensure scope matches the attribute name
  • all other circumstances return true (ex. characterData, which as of 2024/12/15 isn't implemented)
OBSERVER.mjs
OBSERVER.mjs

DOM

PropertyTypeDescription
DOMStateWeakMap.<Element.childNodes>

When DOM.populate mutates an element containing initial DOM content (non-empty) its held here. I leave it to the philosophers to decide if this means we use a VDOM πŸ™Š

// TODO To be integrated/moved into core state controller once implemented?

Function

compareWithOriginalState(element, originalState)β‡’ boolean

Compare a DOM element with its stored original state

Returns: boolean β‡’ True if element matches original state

ParameterTypeDescription
elementElement

Current element

originalStateDocumentFragment

Original state fragment

compareNodes.mjs

classAdd(el, value)

Add CSS class (minimize mutation)

ParameterTypeDescription
elHTMLElement
valuestring

Class name

DOM_classAdd.mjs

classRemove(el, value)

Remove CSS class (minimize mutation)

ParameterTypeDescription
elHTMLElement
valuestring

Class name

DOM_classRemove.mjs

removeChildren(target)

The performant alternative to innerHTML=''.

2022/03/15 Perf. between remove() and removeChild() is a wash.

ParameterType
targetNode
elements.mjs

appendChildren(target, origin, [clone])

Appends all target children from the origin node, cloned by default.

ParameterTypeDefaultDescription
targetNode
originNode
[clone]booleantrue

Uses .cloneNode(true) to attempt a deep clone πŸ‘

elements.mjs

escape(text)

Escape text using the browser's built-in functionality.

If this fails to escape content it means the browser is vulnerable, which is both crazy AND not our problem, horray!

ParameterType
textstring
elements.mjs

unescape(text)

Inverse of escape.

ParameterType
textstring
elements.mjs

meta(name, [content])β‡’ HTMLMetaElement

Returns existing or creates new <meta> tag in <head>, optionally modifying content attribute.

ParameterType
namestring
[content]string
elements.mjs

link(rel, [href], [media])β‡’ HTMLLinkElement

Mutates or creates (if no match) the first found <link> element within <head> by rel attribute, UNLESS it's a stylesheet - which also matches href attribute.

ParameterTypeDescription
relstring

Relation type https://html.spec.whatwg.org/multipage/links.html#linkTypes

[href]string
[media]string
elements.mjs

escapeCSS(inputStr, [options])

Wraps native CSS.escape() function to warn developers if input is modified when escaped.

category helper
ParameterType
inputStrstring
[options]object
[options.throwIfEmpty]boolean
escapeCSS.mjs

loadScript(src, [options])β‡’ Promise

Append a script to the DOM and return loading status to an optional callback.

StageNeeds Refactor or Deprecation

Asynchronous and removes own script element after loading by default. This was written before the days of wide browser module support and import / import(). Deprecation likely unless a specific use case is found.

ParameterTypeDefaultDescription
srcstring

relative or exact URL.

[options]object
[options.callback]function

Passed boolean based on loading success

[options.async]booleantrue

Asynchronous loading

[options.defer]boolean

Deferred - executed after the DOM has been parsed, but before firing DOMContentLoaded

[options.remove]booleantrue

Remove script element after loading attempt 🧹

[options.crossorigin]string

If unspecified use-credentials is automatically applied when src is considered local (see net/isLocal). 'anonymous'|'use-credentials'.

[options.integrity]string

SRI hash

[options.module]booleantrue

Sets type="module" attribute

[options.nomodule]boolean

Sets nomodule attribute, for scripts to run in pre-ES2015 compatible browers.

loadScript.mjs

loadStyle(styleTagID, options)β‡’ HTMLStyleElement

CSS Style loader.

taxonomy DOM, CSS, loader, mutator
StageWIP

Should scoping be added to go with modularity?

Creates a new element if no matching style element exists.

ParameterType
styleTagIDElement.id
optionsObject
options.innerTextHTMLStyleElement.innerText
loadStyle.mjs

populate(root, obj, [options])

Populate DOM nodes from JS objects, recursively.

AKA
hydrator, hydration

Non-object keys are matched to DOM elements via the following rules:

  1. Elements with 'name' attribute:
  • Inexact key match via [name$="key"]
    • (When nested) Exact key match [name="parentKey[key]"] within nested arrays/objects
  1. Elements with any class names starting with pd_
  2. (WIP) Class directly matching the nested child object key's name (no 'pd_')

pd_* classes are preferred over data attributes because:

  1. CSS selectors for formatting are cleaner ( #pages .pd_chainImg vs #pages [data-pd=chain_IMG] )
  2. Classes come out ahead of data attributes in query performance tests

Default string, number, boolean type value mutations:

  • <input> and <select> form fields (radio, checkbox, select)
  • <time> add relative attribute to generate a relative locale time
  • <textarea> updates vertical sizing (may be moved out of CORE)
  • All others element's content is replaced as PLAIN TEXT. When replacing a non-empty element, its INITIAL content (content not added by the populator) is retained. This content is restored on future calls with an empty string value (or via pd.DOM.reset), allowing for 'default content' in non-input elements. This MAY become a non-default boolean option in a future release - given feedback.

Built-in object mutation keys:

  • _readOnly[] applies readonly attribute to INPUT,TEXTAREA elements, and readonly CSS helper class to parent LABEL element if one exists. Applies readonly CSS class directly to any other located elements.
  • _disabled[] Applies disabled attribute to INPUT,TEXTAREA,BUTTON elements, as well as disabled helper CSS class to parent LABEL if it exists. Applies disabled CSS class to any other located elements.
ParameterTypeDefaultDescription
rootstring | Element

input name prefix eg. prefix_Item, or specific DOM element. For better performance specify a Node to allow direct querying when populating a single element and its children, a string is useful when targeting multiple root elements. // DOC unsure if Node is sufficient, Object?

objObject.<string, any>

Object containing new state. As of 2022/07/20 DOM.populate doesn't allow naked (unnested) arrays, because keys are necessary for DOM query matching. Place the array inside an Object, or (not recommended) hijack private nested options parameter.

[obj._readOnly]Array.<string>

(EXPERIMENT) Special built-in key.

[obj._disabled]Array.<string>

(EXPERIMENT) Special built-in key.

[options]object
[options.maxDepth]number10

Maximum allowable nested object recursion depth. If reached populator short circuits and returns, preventing additional processing.

[options.setDefaultInputs]Boolean

Permanently replace default input defaults (useful for form resets), this MAY become a special built-in similar to _disabled object key

[options.template]'append' | 'replace''replace'

'replace' removes existing elements generated from templates, 'append' retains them.

[options.cascade]Boolean

(EXPERIMENT) (DANGER) Allow simulated interactions to cascade naturally, test to prevent infinite loops!

[options.event]Event

(EXPERIMENT) Likely to be removed. Skips processing of elements matching original event target. Improve UX by allowing populator to skip current target element when populating due to a user event.

[options.nested]string

(PRIVATE) Used internally during nested object recursion, usually grouped form inputs.

populate.mjs

stringToSelector(str, [extras])β‡’ string β„—

Simple string to CSS selector text transformer.

DOESN'T guarantee validity, this transformer has no deep CSS sanity checking.

actually CSSStyleRule['selectorText'] for various params, but JSDoc rendering chokes.

Returns: string β‡’ ordered by CSS specificity

Primarily for transforming object keys into useful generic CSS selectors.

ParameterTypeDefaultDescription
strstring
[extras]string''

Appended after to each selector

query.mjs

elementTitle(el)β‡’ string | boolean

Human readable title from data-name attribute or derived via id attribute.

Returns: string | boolean β‡’ invalid element returns false boolean. Empty string when no useful attribute is found.

When no data-name attribute is found a string is created from the id attribute by replacing - and _ with spaces, trimming whitespace, and capitalizing the first character.

ParameterType
elHTMLElement
query.mjs

queryFilter(selector, [root], [nestSelectors])β‡’ Array.<Element>

Provides elements located within root(s) and recursion chain.

actually CSSStyleRule['selectorText'] not just basic strings

Returns: Array.<Element> β‡’ Actually could be any sorta Element, no?

ParameterTypeDefaultDescription
selectorstring
[root]Element | stringdocument.body

Selector string allows for multiple roots!

[nestSelectors]Array.<string>[]
query.mjs

querySelectParent() β„—

Moved from query.mjs as reference for refactor since it references

Deprecated

reset_DEPRECATED.mjs

reset(parentSelector, [options])

Reset DOM/input state of a node's children

Deprecated

StageNeeds Refactor or Deprecation
ParameterTypeDescription
parentSelectorObject | string
[options]object

form, readonly, inputs, defaults, hidden, selectNone. If none are used, all options are true.

options.formboolean

reset all inputs in a form

options.toDefaultboolean

input fields set to default value

options.toInitboolean

restores non-form elements to initial state. See #166

options.listsboolean

sponsors / interests? Removes from DOM entirely.

options.readonlyboolean

remove readonly attribute and class EXCEPT hidden inputs

options.selectionboolean

revert checked/selected items to blank state

options.defaultsboolean

revert checked/selected items back to original state

options.hiddenboolean

hidden element values EXCEPT 'readonly'

options.selectNoneboolean

simulates click of inputs with "none" value (usually radio inputs)

options.inputBackgroundboolean

text input backgrounds

reset_DEPRECATED.mjs

temporary(target, attribute, [value], [options])β‡’ DOM.temporary.domTemporaryState

Temporarily mutate HTML element attributes.

Currently limited to a single outstanding mutation per target at a time, to allow more would require a Set keyed by `attribute` (& `value`, if class or similar) within the target keyed weakMap.. Feel free to submit a PR or request this feature in the future.

Useful UX Helper that can be used to temporarily add animation classes, disabling buttons, etc.

ParameterTypeDefaultDescription
targetHTMLElement
attributestring

class, disabled etc...

[value]string''

optional attribute value.

[options]object
[options.duration]number

in seconds, derived from computed animation duration (if any) if not supplied.

[options.delay]number

in seconds

Typedef

domTemporaryState object

PropertyTypeDescription
timerIDnumber

Timer ID provided by timeout API

attributestring
[value]string
delaynumber
durationnumber
statusnumber

0 pending, 1 complete, -1 aborted

temporary.mjs
temporary.mjs

textarea([height], [width], [minHeight])

Dynamically resize textarea elements to content.

// TODO externalized to module? // TODO Output as --pd-textarea-height to allow custom styling?

ParameterTypeDefaultDescription
[height]booleantrue

When false sets --scrollHeight CSS variable on element.

[width]boolean
[minHeight]number50

Minimum fallback height

textarea.mjs

EVENT

2022/02/20 when minifying bound function names **MUST BE PRESERVED**, otherwise the event filter breaks catastrophically!

ideally EVENT becomes an interface of sorts that allows it be a delegator in other instances such as Node/native?

PropertyTypeDescription
isKbdboolean

Is this a keyboard event?

  • true for KeyboardEvent
  • false for MouseEvent and TouchEvent
  • null for all other events EXCEPT FocusEvent, where it retains its previous state.
priorEventsArray.<(Event|undefined)>

Previously captured events (currently hardcoded as 10).

lastEventTrustedboolean | null

Browser native event.isTrusted is untrustworthy, use this instead!

For whatever reason event.isTrusted becomes true when:

  • a new MouseEvent is dispatched without an eventInitDict parameter. It becomes trusted after dispatch! This may be true for other artificially created events as well.
  • Follow-up events (input/change) to untrusted form input click events become trusted.

Please see this CodePen for demonstration. Reach out to core team if this is a known "wontfix", or are working on an eventual solution allowing us to remove it.

activeListenersArray.<string>

Array of active DOM event types with listeners added via bindAdded to document, which call pd.EVENT.handler.

Function

add(eventName) ⊨add β„—

Adds document element event listeners as necessary

Extends: BIND.bind

Implements: add

ParameterTypeDescription
eventNameArray.<string>

Actually <Event['type']> or similar

EVENT.mjs

disable(eventName) ⊨disable β„—

Removes document element event listeners when no more EVENT binds utilizing them remain.

Extends: BIND.disableBindByID

Implements: disable

ParameterTypeDescription
eventNameArray.<string>

Event['type']

EVENT.mjs

filter(_trigger, event, iterator) ⊨{}β‡’ boolean β„—

Implements: {}

ParameterType
_trigger*
eventEvent
iteratorNumber
EVENT.mjs

eventHandler(event)

DOM/client Event handler.

I have a suspicion I poorly abstracted the Event API here and would love to refactor more tightly into the existing Event API if reasonable sensible. The core reason of this handler's existence is to execute bound functions matching incoming events.

Freezing may occur due to unintended infinite simulated event loops. If an event triggers a handler or other code that then toggles or reverses the current state being set, an infinite loop may occur due to simulated events.

EVENT binds must be named functions.

  1. Set EVENT.lastEventTrusted helper
  2. Set EVENT.isKbd helper
  3. Event binding filter returns a boolean based on the following conditions:
    1. The Event source is trusted OR allowed to cascade.
    2. AND if bind is scoped, it is the Event target OR within the target (closest).
    3. AND Event type matches one of the bind's triggers.
    4. AND ANY of the following:
      1. (KLUDGE) Function name begins with _, which is considered untargeted 'global'.
      2. OR function name exactly matches element's ID.
      3. OR function name matches an HTML tag name within the parent Element chain (nodeNames are ALL CAPS). This check is skipped when the event directly targets an element without a parent node, such as the document root, since they don't have the 'closest' function.
      4. OR function name matches the name attribute in the parent Element chain.
      5. (EXPERIMENT/HACK) OR if target is an anchor link, href attribute beginning with #bind-.
ParameterType
eventEvent
EVENT.mjs
EVENT.mjs

event/simulateEvent

Simulate user events via synthetic event

event_simulate.mjs

HELPER β„—

PageDriver CORE team internal helper functions.

Consider alternatives or local copy (with appropriate copyright notices) if anticipating external use.

They may be removed or have breaking changes between minor versions.

Function

convertMS(ms)β‡’ object

Convert milliseconds to { days, hours, minutes, seconds } object.

2022/03/19 Only used by `storage/clearOldLocalStorage`, may remove/refactor in favour of native Date unless this finds use elsewhere.
ParameterTypeDescription
msnumber

milliseconds

convertMS.mjs

formatTimeAgo(date)β‡’ string

Returns a localized, relatively formatted date string

Modified version of formatter by Kyle Cook

ParameterType
dateDate
formatTimeAgo.mjs

importLoader(src)β‡’ Promise

Imports a script into a globalThis variable named via its file name.

This function throws an error outside the 'Development' environment.

Returns: Promise β‡’ import() promise.

Primarily intended for developer exploration of scripts via a live console such as DevTools, especially modules normally hidden from the global scope. How annoying!

  • Multiple exports: module is assigned.
  • Single export: single export assigned.
ParameterTypeDescription
srcstring

'/src/' prepended.

importLoader.mjs

mergeDeep(objects)β‡’ object

Performs a deep merge of objects and returns new object.

Returns: object β‡’ New object with merged key/values

Doesn't modify objects (immutable) and merges arrays via concatenation.

2022/09/06 Being used for merging settings/remote at initiation, likely useful for later sync work :)

Consider https://github.com/jhildenbiddle/mergician for advanced merges

ParameterTypeDescription
objectsobject

Objects to merge

mergeDeep.mjs

simpleHash(str)β‡’ number

General-use hasher.

category helper

Returns: number β‡’ Integer hash (currently CRC24)

Using this for cryptography will result in being shot from a cannon into low earth orbit ✨🌎

ParameterType
strstring
simpleHash.mjs

truthy(value)β‡’ boolean

Coerce human readable truthy values to boolean (true/false)

category helper, i18n i18n 'iya' (Indonesian) WON'T return true. Conflict with Japanese いや romanji. May be added later if this function checks server or user's active use language. contribution 'true' (English) is common to most programming languages. Other 'true' variations may be added ONLY if commonly used in code - otherwise only localized 'yes' equivalents are accepted. Contributed 'yes' strings: - MUST be one of top 25 used languages - MUST be verified as non-negative in ALL other ACTIVELY used languages. - SHOULD be ordered by most likely to match (eg. 'Internet users by language').
ParameterType
valueboolean | string | number
truthy.mjs

INIT

Self-deleting project initializer.

EditionCORE

The following is likely out of date, eventually this list will be automatically rendered based on inline @step and @stepOpts:

  1. Start performance measurement
  2. Debug state check, when debug cookie is present load and initialize debug tools.
    • Otherwise, execute optional app.notDebugging() function (to do things such as displaying a default console message/warning)
  3. Setup remote logging. If pd.SITE.ENV is Production, initialize logger based on pd.SITE.remoteReporting object. Can be forcibly enabled via forceLogging cookie in other environments
  4. Set pd.SITE DOM references and booleans
  5. Context class setup
  6. i18n initialization
  7. pd.DOM elements (most removed for upcoming modularity)
    • closeable
  8. Attempt initializing app.js via app.INIT() (/w perf measurement)
  9. pd.SITE.location
  10. End performance measurement
  11. Destroy pd.INIT
init.mjs

modal

Function

activateModal(req)β‡’ boolean

Activate a modal

ParameterTypeDescription
reqstring

HTML ID attribute

modal_activateModal.mjs

close([_event], [force])β‡’ boolean

Attempt closing modal.

Returns: boolean β‡’ false when no modals exists, no modal is active, or if the modal cannot be closed without force.

If the modal's first child isn't a closeBtn button, it will fail and temporarily add the attention class to the modal element. force is true.

ParameterTypeDescription
[_event]Event

Currently unused.

[force]boolean

forcibly close modal that does not have

modal_close.mjs

currentActiveModal()β‡’ HTMLElement | null

Active modal, or null if none.

Only one modal is active at any given time (assuming nothing broke).

modal_currentActive.mjs
modal_init.mjs

lastIsLocalSrcResult Array.<string, boolean> β„—

Memoization short circuit for pd.NET.isLocal.

I never checked if this actually improves performance, but it probably helps? Trivial to implement and just as trivial to remove if deemed unnecessary! [src URL, isLocal result]

net_isLocal.mjs

submitTimeout delayState | null β„—

Internal placeholder for outstanding form submit button mutations, likely to be refactored to allow multiple concurrent form submissions.

Stage2 WIP
net_submitTimeout.mjs

NET

Network related functions and helpers.

Function

getAllResponseHeaders(xhr)β‡’ Object.<string, string> | Object

Maps XHR response headers into a KV object.

Returns: Object.<string, string> | Object β‡’ Empty object when no header values are returned.

ParameterType
xhrXMLHttpRequest
net_getResponseHeaderMap.mjs

isLocal(src)β‡’ boolean

Could src be considered local? Note domain-like strings, such as hostnames WITHOUT a protocol such as 'example.com' return true as it could be a valid local path. Returns false when src string begins with 'https://', 'http://' or '//', UNLESS it matches the current window.location.origin.

ParameterTypeDescription
srcstring

URL, path, etc...

Example

islocal('/example'); // true
islocal('example.com'); // true
islocal('https://example.com'); // false
islocal('//example.com'); // false
net_isLocal.mjs

spinner() β„—

DOM spinner control

Stage2 WIP
net_spinner.mjs

xhr(src, [callback], [options])β‡’ XMLHttpRequest

Perform an XHR, optionally returning status/object to callback

Stage2 WIP
Doesn't handle HTTP error codes appropriately yet!

Returns: XMLHttpRequest β‡’ passes responseObj, src URL to provided callback

  • Automatically applies pd-csrf and pd-app-version headers to local requests
  • Under Development ENV the noStore option is true on all local requests to ensure freshness. Create a cookie named 'pd-debug-breakLocalCache' to disable this feature.

Why XHR and not Fetch?

  • Fetch doesn't track upload progress natively
  • Once timeouts, abort controls and other features are added, Fetch feels about the same.
  • It's what I knew best when writing PageDriver, feel free to contribute a PR, especially once work begins on ServiceWorkers!
ParameterTypeDefaultDescription
srcstring

Remote URL or local path. Specificity is important! Project's API path is prepended for relative paths NOT starting with a slash /. Ex. foo prepends API path, whilst /foo doesn't.

[callback]function
[options]object
[options.type]'json' | 'head' | 'html' | 'md'json

Expected response type. NOT XMLHttpRequest built-in enum, may eventually be extendable via binds.

[options.spinner]booleantrue

Display spinner

[options.timeout]number10000

Milliseconds, default is 10 seconds

[options.formData]FormData

When included request is sent as 'POST', client's UTC offset and current active PD page are automatically appended (subject to change, provide feedback).

[options.noStore]boolean

Adds request headers to forcibly acquire a fresh response

[options.headers]object

Custom headers

net_xhr.mjs
net_xhr.mjs

prior Array.<Array> β„—

Current/prior page IDs & X/Y scroll positions

// HACK all code relating to 'prior' is a god damn mess and needs to be refactored for readability/more use cases

page_activate.mjs

pd

PageDriver core library entry point.

EditionCORE
Stage2 FeedbackNeeded

Code reviewers

  • Too much to take in? Search for comment flags (see ## COMMENT FLAGS below) containing question marks instead and skip to those :p
  • Try not to focus on:
    • swappable/toggleFUQs/selected fns. Being refactored (see issue #16, #205)
    • closeables (along with a bunch of other INIT run-once features) will be refactored to use a bindable mutation observer
    • External settings (pd.SITE etc) are currently generated server side. Ideally they'll be compiled to a mostly static (JSON?) file. To be finalize this by next milestone... advice appreciated.

PageDriver was created to solve the following pain points

  • Too much damn boilerplate, keep code coherent and clean.
  • Bring back some separation of concerns (HTML/CSS/JS). They can be in the same file as part of simple single-file components, but not so intermingled.
  • Callback hell, solved by 'bind' buses (bus maybe the wrong term?)
  • Minimize and clean call stack to assist in debugging.
  • DOM hydration concerns, I want it to follow state on demand!
  • Lack of accessibility. A11y is normally out-of-scope for small to medium sized projects, I want to change that.
  • Requiring a virtual DOM. It does have a use case for repeating elements scrolling outside the viewport (such as tables). I've done testing and it's unnecessary for the majority of projects. Let's keep things close to vanilla!

Contributor's guide

CODE COMMENT GUIDE

  • Negations MUST BE contractions for comprehension and specificity. They MUST be capitalized if critical. Examples: "don't" vs "do not", "won't" vs "will not", "can't" vs "can not" (although "cannot" is OK!)
  • Append relevant repo issue #s, if at all possible.
  • @warning must begin with a date (YYYY/MM/DD) to be checked for continued relevance

COMMENT FLAGS

  • Highlight flags in VSC via 'gruntfuggly.todo-tree' extension. Project settings are included in the repo.
  • WIP for code that is a work in progress and likely broken. Don't commit these.
  • i18n for marking strings requiring translation.
  • SETTINGS for variables eventually managed by project admin/super-admin.
  • BIND for marking areas with 3rd-party extendability (usually bindExec)
  • Once stable, code containing WIP, FIXME and TODO comment flags will be automatically rejected.

CORE DEVELOPER COMMENT FLAGS (don't commit these unless you're a CORE dev)

  • Add FIXMEs to critical, high priority code that you are unable to fix yourself. FIXME code CANNOT be submitted for stable releases!
  • TODO are low priority FIXMEs with an assigned issue (create one if none exists), and may exist in stable releases. These are a temporary measure from initial development efforts.
  • DOC for marking areas requiring external documentation from the documentation team. NOT an alternative to commenting your code.

CORE CODE GUIDE (oldest first)

  • 2020/04/25 [Consistency] 'use strict'; outside ESM modules
  • 2020/04/25 [Compatibility] When using native features, they MUST pass the project's CIU threshold (currently ~95%)*. See https://gitlab.com/evilumb/pagedriver/dev/-/issues/66 for more details
  • 2020/04/25 [Consistency] Iterate HTMLCollections with bracket notation [i], not .item(i) method
  • 2020/04/25 FUTURE DEPRECIATION: Many PageDriver settings are set via project index's state initializer file, this will be improved. #162
  • 2020/04/25 [Mutation] use document.createDocumentFragment(); when the primary objective is reducing total DOM mutations, otherwise measure actual performance, prioritizing code readability.
  • 2020/04/25 [Performance] requestAnimationFrame() can force unnecessary re-rendering, most circumstances do not require it.
  • 2021/02/09 [Consistency] Use Object.assign() instead of spread operator where possible. Spread is 93.05% CIU, 1% slower, and doesn't improve code readability that much.
  • 2021/03/10 [Documentation] Using Regex? Register at https://regex101.com, and provide a link in code commentary along with some match/non-matching samples if possible.
  • 2021/04/03 [Mutation] When directly adding/removing CSS classes, they trigger class attribute mutation even if nothing actually changes (per spec). Use pd.DOM class helpers or check classlist.contains first if mutation is an issue (pd.OBSERVER may ignore these in the future).
  • 2021/04/06 [Consistency] options={} for parameter options, then Object.assign() defaults to params obj internally. Once spread operator passes CIU %, codebase may transition to it. Use deep merge helper for options with complicated nested defaults (although that might be a code smell). JSDOC options props, not params props.
  • 2021/05/03 [Consistency] Object literals > classes. Why? Lorin likes em'. Β―\_(ツ)_/Β―
  • 2022/03/24 [Consistency] Use provided function names instead of local destructuring, to prevent core code bloat.
  • 2022/09/19 [Consistency] Modules exporting single default functions are preferred, however when exporting objects containing a set of functions, the file and module name MUST BE capitalized for easy identification and potential future splitting for improved tree-shaking.
  • Take care iterating HTMLCollections by index if elements are being removed within the loop, as HTML DOM is live!

HINTS

  • [Performance] Short circuit, break early when possible. "The cheapest function call is the one you never make" - Jason Miller, Preact dev.
  • 2022/08/30 [Performance] Converting NodeLists to Array is SLOW. Array.from is slower than spread.
  • 2021/05/03 [DevTools] Alert() is for chumps. Try live expressions, breakpoints, debugger;, or console.log(). See additional helpful utilities here https://developer.chrome.com/docs/devtools/console/utilities/?utm_source=pagedriver&utm_medium=sourcecode
  • 2021/02/16 [Annoyance] HTMLElement.prototype.closest doesn't accept other HTMLElements, just a selector string :|
  • 2021/04/06 [Annoyance] Don't trust event.isTrusted, see notes in pd.EVENT.simulate.
Property
DEBUG

Namespace

SITE object

Most properties will permanently move to other CORE objects once v1 lands

Stage1 Discuss
PropertyTypeDescription
ENV'Production' | 'Development'

Should probably have some overview of the differences eh? :p

contextsArray.<string>

Context classes / flags

[domain]string
[path]string

Root path of PageDriver instance

apistring

Local API path/URI, prepended to local API requests

titlestring

Site title

siteTitleFirstnumber

Site title appears first, useful for RTL

[titleDivider]string

site and page title separator in window title

headerHTMLElement

First found header or #header element

primaryNavHTMLElement

First found nav , #nav or #menu element

mainRegionHTMLElement

Primary content region (body>#main , body>main)

fixedScrollboolean

Does the pages element have fixed scrolling? Is scrolling controlled by pd.SITE.pages element or window?

pagesHTMLCollection

#pages children

modalsHTMLCollection

#modals children

remoteReportingobject

Reporting provider (only sentry.io supported at this time)

remoteReporting.endPointstring

URL

[remoteReporting.integrity]string

integrity hash

remoteReporting.srcstring

reporter bundle location

[description]string

Default site-wide meta tag. Used when none available at page level.

[keywords]string

Default site meta tag.

typedefs.js
pd.mjs

deprecating β„—

Deprecated

Function

swappable(target) β„—

Toggle .swapped class on matching elements within local .swappable class node

Adds .swapped class to an element with an ID matching the combined target input's name and value (capitalized, if possible), after removing it from its siblings.

Example: Target is a selected radio input named 'eventVis' with the value 'public'. An element with the ID 'eventVisPublic' has .swapped class added, and the class is removed from its siblings.

ParameterTypeDescription
targetstring

Input element (Checkbox/Radio)

REPLACEME.mjs

toggleFUs()

Toggle visibility of follow up components

Deprecated

REPLACEME.mjs

selected()

Toggles .selected CSS class on parent element of checked input (checkbox/radio) and toggles related IDs within nearby .swappable element.

Deprecated

If input was added by the user (.userAdded), it is removed from the DOM.

Surround swappable items with a swappable class and give each an ID that is the checkbox/radio name + value with first character capitalized. Ex. a radio named fizz with a value of buzz would trigger an element with the fizzBuzz ID.

Only works if input elements are nested within... 2 elements of each other. Why 2? This functionality was added because I blindly trusted my own HTML, then realized some input elements were unnecessarily nested - now it sits as a reminder to ensure the content you are working with is correct to begin with. Maybe it will come in handy some day.

This trash heap is still necessary in 2021 because CSS doesn't have non-capturing selectors! Why?! WHY CAN'T :has() WORK OUTSIDE OF JS??

REPLACEME.mjs
REPLACEME.mjs

ROUTE

Property
execOnAdd
ROUTE.mjs

app/settings

category app - SETTINGS should be cache friendly. - REMOTE should provide dynamic variables, and minimally cached, if at all. It is merged into settings, overwritting state.
settings.mjs

STORAGE

Cookie functions do not handle edge case where browser creates multiple cookies with the same name under similar domains (ex. `example.com`/`example.com.`).

Function

setCookie(name, [value], [days], [options])

Modify a cookie, yum! πŸͺ

ParameterTypeDefaultDescription
namestring
[value]string
[days]null | number

0 destroys the cookie, null / undefined creates a 'Session' cookie

[options]object
[options.samesite]stringstrict
[options.path]string'/'

Advanced isolation feature. Cookies may behave unexpectedly due to SPA routing. Path must always start with a / slash ...right?

STORAGE.mjs

getCookie(name, [existence])β‡’ string | boolean

Get cookie value or check existence

Returns: string | boolean β‡’ Returns false boolean when non-existent. Returns boolean existence argument is true. Otherwise returns cookie contents string.

ParameterTypeDescription
namestring

Cookie name

[existence]boolean

(default false) If true, returns cookie existence boolean, not value - useful for valueless cookies.

STORAGE.mjs
STORAGE.mjs

_META object

PageDriver metadata standard

πŸ”’ RESERVED
Stage1 Discuss
PropertyTypeDescription
namestring

Human readable project/module name

versionsemver

Semantic versioning code, see https://semver.org/

changelogstring
compliancearray.<string>

'i18n-AA','a11y-AA','GDPR'...

licensestring

shortname ex. 'Apache-2.0'

stableboolean

Considered unstable when undefined or false.

[stage]'1-Discuss' | '1-Needs_champion' | '2-WIP' | '2-Feedback_needed' | '3-Test' | '4-Document' | '5-Shipped' | '6-Needs_Refactor_or_Deprecation'

Current production stage. This doesn't really belong in _META, but some sort of JSDOC/Typedoc metadoc?

edition'CORE' | 'PRO' | 'PLATFORM'

Intended target edition

authorsarray.<UUIDv4>
domainsarray.<'Library', 'Framework', 'Platform'>

Relevant domains. Workers? πŸ€”

[parallel]boolean
[priority]number

(?) Loading prioritization? idk

typedefs.js

addButton(el)β‡’ boolean

Create a close button ... refactor using future template system...?

Returns: boolean β‡’ True when closed via local storage

ParameterType
elHTMLElement
closeable.mjs

mobileMenu

Mobile menu (hamburger) πŸ”

AKA
hamburger

Prepend navigation button for collapsing primary navigation on small devices (hamburger πŸ”)

mobileMenu.mjs

undefined

😯 Uh oh...

A critical error has occurred preventing the site from loading. An error report has been automatically sent for investigation.

If this error is preventing you from completing an important action, please try again later.

Oops!

Search inoperable

We can't seem to find the requested page. If this error is preventing you from completing an important task, please contact us to resolve it.

We're sorry...

Something unexpected happened. If this error is preventing you from completing an important action, please contact us to resolve it.

500 Internal Server Error

Unauthorized access

You do not have the necessary permission to access this data.

401 Unauthorized

Oops!

We can't seem to find the requested resource. If this error is preventing you from completing an important action, please contact us to resolve it.

404 Not Found

Developer Preview

Welcome to the PageDriver 'Library v0' milestone preview. You've been invited at this critical juncture to provide early input and feedback about conventions and functionality. Intuitiveness is a primary project goal.

Please use the feedback tool zealously, for good or bad! Looking forward to reviewing your thoughts and opinions - Lorin

Please note

Getting help

Only community based support options are available at this time. Once officially launched, additional support options may become available.

Please fully review the official reference and guides before reaching out for assistance.

DEV 2025 Jan 08, 02:31am UTC πŸͺ²