@ideal-postcodes/jsutil
Version:
Browser Address Autocomplete for api.ideal-postcodes.co.uk
711 lines (695 loc) • 23.9 kB
TypeScript
import { components } from '@ideal-postcodes/openapi';
type UkSuggestion = components["schemas"]["UkAddressSuggestion"];
type GlobalAddressSuggestion = components["schemas"]["AddressSuggestion"];
type AddressSuggestion = GlobalAddressSuggestion | UkSuggestion;
type PafAddress = components["schemas"]["PafAddress"];
type MrAddress = components["schemas"]["MrAddress"];
type NybAddress = components["schemas"]["NybAddress"];
type PafaAddress = components["schemas"]["PafAliasAddress"];
type WelshPafAddress = components["schemas"]["WelshPafAddress"];
type GlobalAddress = components["schemas"]["GbrGlobalAddress"];
type GbrAddress = PafAddress | MrAddress | NybAddress | PafaAddress | WelshPafAddress | GlobalAddress;
type UspsAddress = components["schemas"]["UspsAddress"];
type UsaGlobalAddress = components["schemas"]["UsaGlobalAddress"];
type UsaAddress = UspsAddress | UsaGlobalAddress;
type AnyAddress = GbrAddress | UsaAddress;
interface Binding {
/**
* Verify if can be launched
*/
pageTest: PageTest;
/**
* Binds to page
*/
bind: Bind;
}
interface Start {
(config?: Config): number | null;
}
interface Stop {
(): void;
}
interface Bind<T extends Config = Config> {
(config?: T): void;
}
interface PageTest {
(): boolean;
}
interface Selectors {
line_1: string;
line_2?: string;
line_3?: string;
post_town: string;
county?: string;
postcode: string;
organisation?: string;
country: string;
}
interface Config {
/**
* Enable / disable the integration altogether
*/
enabled: boolean;
/**
* A string of characters. Typically begins `ak_`
*
* The API Key is required to verify your account for address validation. View your API Keys from your [account dashboard](https://ideal-postcodes.co.uk/tokens).
*
* See our [API Key guide](https://ideal-postcodes.co.uk/guides/api-key) to find out more.
*/
apiKey: string;
/**
* Populate the county field of an address. County data is optional for premise identification.
*
* We recommend avoid using county data altogether. Find out more from our [UK county data guide](https://ideal-postcodes.co.uk/guides/county-data).
*/
populateCounty: boolean;
/**
* Setting this to `true` will enable autocomplete integration on your address forms.
*
* See our [Address Finder Demo](https://ideal-postcodes.co.uk/address-finder) to try it out.
*/
autocomplete: boolean;
/**
* *Advanced.* Override the autocompletion plugin configuration.
*
* Pass in a [valid configuration object](https://ideal-postcodes.co.uk/documentation/ideal-postcodes-autocomplete#config) to override specific autocomplete configuration attributes.
*/
autocompleteOverride: AutocompleteConfig;
/**
* Setting this to `true` will enable postcode lookup integration on your address forms.
*
* See our [Postcode Finder Demo](https://ideal-postcodes.co.uk/postcode-finder) to try it out.
*/
postcodeLookup: boolean;
/**
* *Advanced.* Override the postcode lookup plugin configuration.
*
* Pass in a [valid configuration object](https://ideal-postcodes.co.uk/documentation/jquery-plugin) to override specific postcode lookup configuration settings.
*/
postcodeLookupOverride: PostcodeLookupConfig;
/**
* Setting this to `true` will detach our Address Validation tools from the page if an unsupported terrority is selected
*/
watchCountry?: boolean;
/**
* Setting this to `true` will attempt to deploy the Address Finder on an input away from line_1
*/
separateFinder?: boolean;
}
/**
* A map of HTML elements to be populated with address data
*/
interface Targets {
line_1: HTMLElement | null;
line_2?: HTMLElement | null;
line_3?: HTMLElement | null;
post_town: HTMLElement | null;
county: HTMLElement | null;
postcode: HTMLElement | null;
organisation: HTMLElement | null;
country: HTMLElement | null;
}
/**
* Placeholder
*/
type AutocompleteConfig = any;
type PostcodeLookupConfig = any;
type LineCount = 1 | 2 | 3;
/**
* Describes availability of specific on page assets
* complete - Asset is either not required or loaded
* loading - Asset being retrieved or parsed
*/
type LoadState = "complete" | "loading";
/**
* Method used to test whether a DOM element qualifies as parent
*/
interface ParentTest {
(e: HTMLElement): boolean;
}
/**
* OutputFields can be identified by the address attributes supported by the API
*
*/
type OutputFields = Partial<Record<keyof GbrAddress, SelectorNode>>;
/**
* USA can be identified by the address attributes supported by the API
*
*/
type UsaOutputFields = Partial<Record<keyof UsaAddress, SelectorNode>>;
/**
* NamedFields - Elements can only be identified using string arguments
*/
type NamedFields = Partial<Record<keyof GbrAddress, string>>;
/**
* Represents either a HTMLInputElement or a selector pointing to one. Element must have a `value` getter/setter available
*/
type SelectorNode = string | HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement;
declare const isGbrAddress: (address: AnyAddress) => address is GbrAddress;
/**
* Returns an object of attributes mapped to HTMLInputElement
*
* Returns null if any critical elements are not present (line 1, post_town, postcode)
*
* @deprecated
*/
declare const getTargets: (parent: HTMLElement, selectors: Selectors) => Targets | null;
/**
* Given a list of bindings, return true if any are relevant to current pae
*/
declare const relevantPage: (bindings: Binding[]) => boolean;
/**
* Setup defaults
*/
declare const defaults: Config;
declare const config: () => Config | undefined;
/**
* Configure GenerateTimer
*/
interface GenerateTimerOptions {
/**
* Function which decides whether to execute bind on each tick
*/
pageTest: PageTest;
/**
* Method to invoke on each tick
*/
bind: Bind;
/**
* Specificy time between ticks in milliseconds
*
* @default 1000
*/
interval?: number;
}
interface TimerControls$1 {
start: Start;
stop: Stop;
}
interface GenerateTimer {
(options: GenerateTimerOptions): TimerControls$1;
}
/**
* Generates a stoppable timer which invokes `bind` on every tick
*/
declare const generateTimer: GenerateTimer;
interface SetupOptions {
window: Window;
bindings: Binding[];
callback?: (result?: SetupResult[]) => void;
}
interface Setup {
(options: SetupOptions): void;
}
interface SetupResult {
binding: Binding;
start: Start;
stop: Stop;
}
/**
* Deploys address search tools on page using predefined bindings
*/
declare const setup: Setup;
interface SetupBindOptions {
selectors: Selectors;
/**
* Query selector that defines anchor. Defaults to selectors.line_1
*/
anchorSelector?: string;
/**
* Restricts subsequent selector scope once anchor is found. Defaults to `form`
*/
parentScope?: string;
/**
* Allow search in specific document
*/
doc?: Document;
/**
* Optional test to select for parent/scope
*/
parentTest?: ParentTest;
}
interface SetupBind {
(options: SetupBindOptions): PageBindings[];
}
interface PageBindings {
anchor: HTMLElement;
targets: Targets;
parent: HTMLElement;
}
declare const setupBind: SetupBind;
declare const toId: (elem: HTMLElement) => string;
declare const cssEscape: (value: string) => string;
/**
* Returns true if window present
*/
declare const hasWindow: () => boolean;
/**
* Convert a NodeList to array
*/
declare const toArray: <T = HTMLElement>(nodeList: NodeList) => T[];
/**
* Returns true if initialised
*/
declare const loaded: (elem: HTMLElement, prefix?: string) => boolean;
/**
* Marks HTML as loaded. i.e. Address validation plugin has been bound to this element
*/
declare const markLoaded: (elem: HTMLElement, prefix?: string) => void;
/**
* Retrives a parent by tag name
*
* Accepts node as a possible candidate for parent
*
* Executes an additional test if specified
*/
declare const getParent: (node: HTMLElement, entity: string, test?: ParentTest) => HTMLElement | null;
/**
* Queries `parent` for a specific `selector`. Returns null if no selector
*/
declare const toHtmlElem: (parent: HTMLElement, selector?: string) => HTMLElement | null;
/**
* Retrieves an anchor defined by a query selector
*
* Checks if anchor has been loaded, if not, marks it as loaded
*/
declare const getAnchors: (selector: string, d?: Document) => HTMLElement[];
interface InsertBeforeOptions {
elem: HTMLElement;
target: HTMLElement;
}
interface InsertBefore {
(options: InsertBeforeOptions): HTMLElement | undefined;
}
/**
* Inserts element before `target` element
*/
declare const insertBefore: InsertBefore;
/**
* Scoped retrieval of a HTML element given a querystring or the element itself
*
* @hidden
*/
declare const toElem: (elem: SelectorNode | null, context: HTMLElement | Document) => HTMLElement | null;
/**
* Outputs scope of controller
* - If null, return window.document
* - If string, resolve with query selector
* - Otherwise returns the HTMLElement or Document
*/
declare const getScope: (scope: null | string | HTMLElement | Document) => HTMLElement | Document;
/**
* Retrieves document instance of HTML Element or Document
*
* Defaults to window.document
*/
declare const getDocument: (scope: HTMLElement | Document) => Document;
type CSSStyle = Partial<Record<keyof CSSStyleDeclaration, string>>;
/**
* Applies style object to HTML Element and returns the previous style in `string` format
*/
declare const setStyle: (element: HTMLElement, style: CSSStyle) => string | null;
declare const restoreStyle: (element: HTMLElement, style: string | null) => void;
/**
* Hide HTML Element
*/
declare const hide: <T extends HTMLElement = HTMLElement>(e: T) => T;
/**
* Show HTML Element
*/
declare const show: <T extends HTMLElement = HTMLElement>(e: T) => T;
/**
* Remove node from DOM
*/
declare const remove: (elem: HTMLElement | null) => void;
/**
* Retrieves DOM Elements and returns the first which matches text
*/
declare const contains: (scope: HTMLElement | Document, selector: string, text: string) => any;
/**
* Find the deepest common ancestor element shared by all provided elements
*
* This function traverses the DOM tree from each element up to the root,
* then identifies the deepest element that is an ancestor to all provided elements.
*
* @param elements - Array of HTML elements to find the common parent for
* @returns The deepest common ancestor element, or null if the array is empty.
* If only one element is provided, returns its parent element (or the element itself if it has no parent).
*
* @example
* ```typescript
* const div1 = document.getElementById('child1');
* const div2 = document.getElementById('child2');
* const commonParent = findCommonParent([div1, div2]);
* // Returns the closest element that contains both div1 and div2
* ```
*/
declare const findCommonParent: (elements: HTMLElement[]) => HTMLElement | null;
declare global {
interface Window {
IdealPostcodes: any;
jQuery: any;
}
}
declare const clearCache: () => void;
interface Downloader {
(d?: Document): HTMLScriptElement;
}
/**
* Script downloader factory. Caches script reference, only downloads once
*/
declare const downloadScript: (url: string, integrity: string) => Downloader;
/**
* Inject CSS stylesheet
*/
declare const loadStyle: (href: string, document: Document) => HTMLLinkElement;
/**
* Inject script tag
*/
declare const loadScript: (src: string, integrity: string, document: Document) => HTMLScriptElement;
/**
* Injects styke element to page
*/
declare const injectStyle: (css: string, document: Document) => HTMLStyleElement;
declare const toCountry: (address: AnyAddress) => string;
/**
* Updates a country field regardless of whether its an input or select
* - If input is detected, value is set to country. For GBR countries, this
* is set to "United Kingdom"
* - If select is detected, the option with the value matching country is
* selected. It will try full name, then iso2 then iso3
*/
declare const updateCountry: (select: HTMLElement | null, address: AnyAddress) => void;
interface EventOptions {
event: string;
bubbles?: boolean;
cancelable?: boolean;
}
interface NewEvent {
(o: EventOptions): Event;
}
/**
* Generates `Event` instance
*
* Includes polyfill where window.Event not available
*/
declare const newEvent: NewEvent;
/**
* Input events we support
*/
type Events = "change" | "input" | "select";
/**
* Dispatch custom event on element
*/
declare const trigger: (e: HTMLElement, event: Events) => boolean;
/**
* Returns true if element is select
*/
declare const isSelect: (e: HTMLElement | null) => e is HTMLSelectElement;
/**
* Returns true if Element is <input>
*/
declare const isInput: (e: HTMLElement | null) => e is HTMLInputElement;
/**
* Returns true if Element is <textarea>
*/
declare const isTextarea: (e: HTMLElement | null) => e is HTMLTextAreaElement;
declare const isInputElem: (e: HTMLElement | null) => e is HTMLInputElement | HTMLTextAreaElement;
declare const update: (input: HTMLElement | null | undefined, value: string, skipTrigger?: boolean) => void;
declare const hasValue: (select: HTMLElement, value: string | null) => boolean;
/**
* Returns array of HTMLOptionElement if textContent matches search value
*/
declare const optionsHasText: (select: HTMLElement, value: string | null) => HTMLOptionElement[];
type InputElement = HTMLSelectElement | HTMLInputElement | HTMLTextAreaElement;
interface SetValue {
(e: InputElement, value: string): void;
}
/**
* Sets the value of an Element using the setter for value found on the
* protype chain
*
* We abandon all hope for input.value = "foo". This is broken on react
*/
declare const setValue: SetValue;
interface Change {
(options: ChangeOptions): void;
}
interface ChangeOptions {
e: InputElement;
value: string | null;
skipTrigger?: boolean;
}
/**
* Updates value of input or select field and triggers an update event
*
* https://github.com/facebook/react/issues/11488#issuecomment-558874287
*/
declare const change: Change;
declare global {
interface Window {
idpcGlobal?: IdpcGlobal;
}
}
/**
* Global store. Aimed at ensuring multiple copies of our JS libs can have some shared state if required
*/
interface IdpcGlobal {
idGen?: Record<string, number>;
}
declare const idpcState: () => IdpcGlobal;
/**
* Resets global store
*/
declare const reset: () => void;
interface IdGen {
(): string;
}
/**
* Generates a globally unique ID
*/
declare const idGen: (prefix?: string) => IdGen;
/**
* Returns number of lines
*/
declare const numberOfLines: (targets: Targets) => LineCount;
/**
* Removes empty fields and joins
*/
declare const join: (list: (string | unknown)[]) => string;
declare const charArrayLength: (arr: string[]) => number;
declare const toMaxLengthLines: (l: string[], options: MaxLengthOptions) => [string, string, string];
type MaxLengths = [number?, number?, number?];
interface MaxLengthOptions extends MaxLineConfig {
lineCount: number;
}
interface MaxLengthOptions {
}
declare const toAddressLines: (lineCount: LineCount, address: AnyAddress, options: MaxLineConfig) => [string, string, string];
type KeysOfUnion<T> = T extends unknown ? keyof T : never;
/**
* Get address property value
*/
declare const extract: (a: AnyAddress, attr: KeysOfUnion<AnyAddress>) => string;
/**
* Retrives fields based on selectors, names and labels
*
* - Highest precedence is given to labels
* - Next highest to names
* - Next to outputfields
*/
declare const getFields: (o: PopulateAddressOptions) => OutputFields;
interface MaxLineConfig {
lines?: LineCount;
maxLineOne?: number;
maxLineTwo?: number;
maxLineThree?: number;
}
interface PopulateConfig extends MaxLineConfig {
scope: HTMLElement | Document;
removeOrganisation?: boolean;
populateCounty?: boolean;
}
interface PopulateAddressOptions {
outputFields: OutputFields;
names?: NamedFields;
labels?: NamedFields;
address: AnyAddress;
config: PopulateConfig;
}
interface MutateConfig extends MaxLineConfig, Pick<PopulateConfig, "removeOrganisation" | "populateCounty"> {
populateCounty?: boolean;
}
interface PopulateAddress {
(options: PopulateAddressOptions): void;
}
declare const searchFields: (outputFields: OutputFields, scope: HTMLElement | Document) => OutputFields;
/**
* Retrieve DOM elemebts by name or similar attributes
* - Checks name first
* - Checks aria-name second
*/
declare const searchNames: (names: NamedFields, scope: HTMLElement | Document) => OutputFields;
/**
* Retrives DOM elements by their labels
*/
declare const searchLabels: (labels: NamedFields, scope: HTMLElement | Document) => OutputFields;
interface MutateAddress {
(address: AnyAddress, config: MutateConfig): AnyAddress;
}
declare const mutateAddress: MutateAddress;
/**
* Insert address values into output fields
*/
declare const populateAddress: PopulateAddress;
/**
* Mutates an address object to remove an organisation name from address
* line and shift up other lines as necessary
*
* - Ignores if only premise identifier is the organisation name
*/
declare const removeOrganisation: (address: GbrAddress) => GbrAddress;
declare const getCountyIsoSelector: (a: OutputFields | UsaOutputFields) => SelectorNode | null;
declare const getCountySelector: (a: OutputFields | UsaOutputFields) => SelectorNode | null;
declare const getCountyIso: (a: AnyAddress) => string;
declare const getCounty: (a: AnyAddress) => string;
declare const isString: (input: unknown) => input is string;
/**
* Returns true if substring match
*/
declare const includes: (haystack: string, needle: string) => boolean;
interface KeyCodeMapping {
[key: number]: SupportedKey;
}
type SupportedKey = "Enter" | "ArrowUp" | "ArrowDown" | "Home" | "End" | "Escape" | "Backspace";
declare const keyCodeMapping: KeyCodeMapping;
declare const supportedKeys: string[];
declare const toKey: (event: KeyboardEvent) => SupportedKey | null;
/**
* Checks if `value` is the
* [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
* of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
*
* @example
* _.isObject({});
* // => true
*
* _.isObject([1, 2, 3]);
* // => true
*
* _.isObject(_.noop);
* // => true
*
* _.isObject(null);
* // => false
*/
declare const isObject: (value: any) => boolean;
/**
* Creates a debounced function that delays invoking `func` until after `wait`
* milliseconds have elapsed since the last time the debounced function was
* invoked. The debounced function comes with a `cancel` method to cancel
* delayed `func` invocations and a `flush` method to immediately invoke them.
* Provide `options` to indicate whether `func` should be invoked on the
* leading and/or trailing edge of the `wait` timeout. The `func` is invoked
* with the last arguments provided to the debounced function. Subsequent
* calls to the debounced function return the result of the last `func`
* invocation.
*
* **Note:** If `leading` and `trailing` options are `true`, `func` is
* invoked on the trailing edge of the timeout only if the debounced function
* is invoked more than once during the `wait` timeout.
*
* If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
* until to the next tick, similar to `setTimeout` with a timeout of `0`.
*
* See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
* for details over the differences between `_.debounce` and `_.throttle`.
*
* @example
*
* // Avoid costly calculations while the window size is in flux.
* jQuery(window).on('resize', _.debounce(calculateLayout, 150));
*
* // Invoke `sendMail` when clicked, debouncing subsequent calls.
* jQuery(element).on('click', _.debounce(sendMail, 300, {
* 'leading': true,
* 'trailing': false
* }));
*
* // Ensure `batchLog` is invoked once after 1 second of debounced calls.
* var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
* var source = new EventSource('/stream');
* jQuery(source).on('message', debounced);
*
* // Cancel the trailing debounced invocation.
* jQuery(window).on('popstate', debounced.cancel);
*/
interface Options {
leading?: boolean;
maxWait?: number;
trailing?: boolean;
}
declare const debounce: (func: any, wait: number, options?: Options) => {
(this: any, ...args: any): any;
cancel: () => void;
pending: () => boolean;
};
/**
* MutationObserver options interface
*/
interface ObserverConfig {
subtree?: boolean;
childList?: boolean;
attributes?: boolean;
attributeFilter?: string[];
attributeOldValue?: boolean;
characterData?: boolean;
characterDataOldValue?: boolean;
}
/**
* Configure GenerateTimer
*/
interface TimerOptions {
/**
* Function which decides whether to execute bind on each tick
* @deprecated
*/
pageTest?: PageTest;
/**
* Method to invoke on each tick
*/
bind: Bind;
/**
* Specificy time between ticks in milliseconds
*
* @default 1000
*/
interval?: number;
/**
* Switching between watching changes via timeouts and mutationObserver functionality
*/
mutationObserver?: boolean;
/**
* Allow to set DOM element to observe changes in it when mutationObserver is set to true
*/
target?: HTMLElement;
/**
* Setting mutationObserver option how observing should be observed
*/
observerConfig?: ObserverConfig;
}
interface TimerControls {
start: Start;
stop: Stop;
}
interface Timer {
(options: TimerOptions): TimerControls;
}
/**
* Generates a stoppable timer which invokes `bind` on every tick
*/
declare const watchTimer: Timer;
declare const watchMutation: Timer;
declare const watchChange: Timer;
export { type AddressSuggestion, type AnyAddress, type AutocompleteConfig, type Bind, type Binding, type CSSStyle, type Config, type Events, type GbrAddress, type GlobalAddress, type GlobalAddressSuggestion, type IdGen, type IdpcGlobal, type LineCount, type LoadState, type MaxLengths, type MaxLineConfig, type MrAddress, type MutateConfig, type NamedFields, type NybAddress, type ObserverConfig, type Options, type OutputFields, type PafAddress, type PafaAddress, type PageTest, type ParentTest, type PopulateAddressOptions, type PopulateConfig, type PostcodeLookupConfig, type SelectorNode, type Selectors, type Start, type Stop, type Targets, type UkSuggestion, type UsaAddress, type UsaGlobalAddress, type UsaOutputFields, type UspsAddress, type WelshPafAddress, change, charArrayLength, clearCache, config, contains, cssEscape, debounce, defaults, downloadScript, extract, findCommonParent, generateTimer, getAnchors, getCounty, getCountyIso, getCountyIsoSelector, getCountySelector, getDocument, getFields, getParent, getScope, getTargets, hasValue, hasWindow, hide, idGen, idpcState, includes, injectStyle, insertBefore, isGbrAddress, isInput, isInputElem, isObject, isSelect, isString, isTextarea, join, keyCodeMapping, loadScript, loadStyle, loaded, markLoaded, mutateAddress, newEvent, numberOfLines, optionsHasText, populateAddress, relevantPage, remove, removeOrganisation, reset, restoreStyle, searchFields, searchLabels, searchNames, setStyle, setValue, setup, setupBind, show, supportedKeys, toAddressLines, toArray, toCountry, toElem, toHtmlElem, toId, toKey, toMaxLengthLines, trigger, update, updateCountry, watchChange, watchMutation, watchTimer };