import { isArray as isa } from "@thi.ng/checks/is-array" ; import { isNotStringAndIterable as isi } from "@thi.ng/checks/is-not-string-iterable" ; import { isString as iss } from "@thi.ng/checks/is-string" ; import { SVG_TAGS } from "@thi.ng/hiccup/api" ; import { css } from "@thi.ng/hiccup/css" ; import { formatPrefixes } from "@thi.ng/hiccup/prefix" ; import { XML_SVG } from "@thi.ng/prefixes/xml" ; import type { HDOMImplementation , HDOMOpts } from "./api.js" ; const isArray = isa ; const isNotStringAndIterable = isi ; const isString = iss ; const maybeInitElement = < T > ( el , tree ) => tree . __init && tree . __init . apply ( tree . __this , [ el , ... tree . __args ] ) ; /**
 * See  HDOMImplementation interface for further details.
 *
 *  param opts param parent param tree param insert export const createTree = < T > ( opts , impl , parent , tree , insert ? , init = true ) : any => { if ( isArray ( tree ) ) { const tag = tree [ 0 ] ; if ( typeof tag === "function" ) { return createTree ( opts , impl , parent , tag . apply ( null , [ opts . ctx , ... tree . slice ( 1 ) ] ) , insert ) ; } const attribs = tree [ 1 ] ; if ( attribs . __impl ) { return ( < HDOMImplementation < any > > attribs . __impl ) . createTree ( opts , parent , tree , insert , init ) ; } const el = impl . createElement ( parent , tag , attribs , insert ) ; if ( tree . length > 2 ) { const n = tree . length ; for ( let i = 2 ; i < n ; i ++ ) { createTree ( opts , impl , el , tree [ i ] , undefined , init ) ; } } init && maybeInitElement < T > ( el , tree ) ; return el ; } if ( isNotStringAndIterable ( tree ) ) { const res = [ ] ; for ( let t of tree ) { res . push ( createTree ( opts , impl , parent , t , insert , init ) ) ; } return res ; } if ( tree == null ) { return parent ; } return impl . createTextElement ( parent , tree ) ; } ; /**
 * See  HDOMImplementation interface for further details.
 *
 *  param opts param parent param tree param index export const hydrateTree = < T > ( opts , impl , parent , tree , index = 0 ) => { if ( isArray ( tree ) ) { const el = impl . getChild ( parent , index ) ; if ( typeof tree [ 0 ] === "function" ) { hydrateTree ( opts , impl , parent , tree [ 0 ] . apply ( null , [ opts . ctx , ... tree . slice ( 1 ) ] ) , index ) ; } const attribs = tree [ 1 ] ; if ( attribs . __impl ) { return ( < HDOMImplementation < any > > attribs . __impl ) . hydrateTree ( opts , parent , tree , index ) ; } maybeInitElement ( el , tree ) ; for ( let a in attribs ) { a [ 0 ] === "o" && a [ 1 ] === "n" && impl . setAttrib ( el , a , attribs [ a ] ) ; } for ( let n = tree . length , i = 2 ; i < n ; i ++ ) { hydrateTree ( opts , impl , el , tree [ i ] , i - 2 ) ; } } else if ( isNotStringAndIterable ( tree ) ) { for ( let t of tree ) { hydrateTree ( opts , impl , parent , t , index ) ; index ++ ; } } } ; param parent param tag param attribs param insert export const createElement = ( parent , tag , attribs ? , insert ? ) => { const el = SVG_TAGS [ tag ] ? document . createElementNS ( XML_SVG , tag ) : document . createElement ( tag ) ; attribs && setAttribs ( el , attribs ) ; return addChild ( parent , el , insert ) ; } ; export const createTextElement = ( parent , content , insert ? ) => addChild ( parent , document . createTextNode ( content ) , insert ) ; export const addChild = ( parent , child , insert ? ) => parent ? insert === undefined ? parent . appendChild ( child ) : parent . insertBefore ( child , parent . children [ insert ] ) : child ; export const getChild = ( parent , child ) => parent . children [ child ] ; export const replaceChild = ( opts , impl , parent , child , tree , init = true ) => ( impl . removeChild ( parent , child ) , impl . createTree ( opts , parent , tree , child , init ) ) ; export const cloneWithNewAttribs = ( el , attribs ) => { const res = < Element > el . cloneNode ( true ) ; setAttribs ( res , attribs ) ; el . parentNode ! . replaceChild ( res , el ) ; return res ; } ; export const setContent = ( el , body ) => ( el . textContent = body ) ; export const setAttribs = ( el , attribs ) => { for ( let k in attribs ) { setAttrib ( el , k , attribs [ k ] , attribs ) ; } return el ; } ; /**
 * Sets a single attribute on given element. If attrib name is NOT an
 * event name (prefix: "on") and its value is a function, it is called
 * with given `attribs` object (usually the full attrib object passed to
 *  setAttribs ) and the function's return value is used as the actual
 * attrib value.
 *
 * Special rules apply for certain attributes:
 *
 * - "style": delegated to  setStyle * - "value": delegated to  updateValueAttrib * - attrib IDs starting with "on" are treated as event listeners
 *
 * If the given (or computed) attrib value is `false` or `undefined` the
 * attrib is removed from the element.
 *
 *  param el param id param val param attribs export const setAttrib = ( el , id , val , attribs ? ) => { if ( id . startsWith ( "__" ) ) return ; const isListener = id [ 0 ] === "o" && id [ 1 ] === "n" ; if ( ! isListener && typeof val === "function" ) { val = val ( attribs ) ; } if ( val !== undefined && val !== false ) { switch ( id ) { case "style" : setStyle ( el , val ) ; break ; case "value" : updateValueAttrib ( < HTMLInputElement > el , val ) ; break ; case "prefix" : el . setAttribute ( id , isString ( val ) ? val : formatPrefixes ( val ) ) ; break ; case "accesskey" : ( < any > el ) . accessKey = val ; break ; case "contenteditable" : ( < any > el ) . contentEditable = val ; break ; case "tabindex" : ( < any > el ) . tabIndex = val ; break ; case "align" : case "autocapitalize" : case "checked" : case "dir" : case "draggable" : case "hidden" : case "id" : case "lang" : case "namespaceURI" : case "scrollTop" : case "scrollLeft" : case "title" : ( < any > el ) [ id ] = val ; break ; default : isListener ? setListener ( el , id . substr ( 2 ) , val ) : el . setAttribute ( id , val === true ? "" : val ) ; } } else { ( < any > el ) [ id ] != null ? ( ( < any > el ) [ id ] = null ) : el . removeAttribute ( id ) ; } return el ; } ; param el param value export const updateValueAttrib = ( el , value ) => { let ev ; switch ( el . type ) { case "text" : case "textarea" : case "password" : case "search" : case "number" : case "email" : case "url" : case "tel" : case "date" : case "datetime-local" : case "time" : case "week" : case "month" : if ( ( ev = el . value ) !== undefined && typeof value === "string" ) { const off = value . length - ( ev . length - ( el . selectionStart || 0 ) ) ; el . value = value ; el . selectionStart = el . selectionEnd = off ; break ; } default : el . value = value ; } } ; export const removeAttribs = ( el , attribs , prev ) => { for ( let i = attribs . length ; i -- > 0 ; ) { const a = attribs [ i ] ; if ( a [ 0 ] === "o" && a [ 1 ] === "n" ) { removeListener ( el , a . substr ( 2 ) , prev [ a ] ) ; } else { el . hasAttribute ( a ) ? el . removeAttribute ( a ) : ( ( < any > el ) [ a ] = null ) ; } } } ; export const setStyle = ( el , styles ) => ( el . setAttribute ( "style" , css ( styles ) ) , el ) ; param el param id param listener export const setListener = ( el , id , listener ) => isString ( listener ) ? el . setAttribute ( "on" + id , listener ) : isArray ( listener ) ? el . addEventListener ( id , ... listener ) : el . addEventListener ( id , listener ) ; param el param id param listener export const removeListener = ( el , id , listener ) => isArray ( listener ) ? el . removeEventListener ( id , ... listener ) : el . removeEventListener ( id , listener ) ; export const clearDOM = ( el ) => ( el . innerHTML = "" ) ; export const removeChild = ( parent , childIdx ) => { const n = parent . children [ childIdx ] ; n !== undefined && parent . removeChild ( n ) ; } ;