2025-03-08 08:16:54 +00:00
|
|
|
import { HTMLNodeTreeWalker } from "../graphs/HTMLNodeTreeWalker";
|
|
|
|
import { DOMEditor } from "./DOMEditor";
|
|
|
|
import { DOMNameSpaces } from "./DOMNameSpaces";
|
|
|
|
|
|
|
|
export class ElementAttribute
|
|
|
|
{
|
|
|
|
static readonly nameAttribute = new ElementAttribute( "name", false );
|
|
|
|
static readonly style = new ElementAttribute( "style", false );
|
|
|
|
static readonly class = new ElementAttribute( "class", false );
|
|
|
|
static readonly id = new ElementAttribute( "id", false );
|
|
|
|
static readonly href = new ElementAttribute( "href", false );
|
|
|
|
static readonly download = new ElementAttribute( "download", false );
|
|
|
|
static readonly target = new ElementAttribute( "target", false );
|
|
|
|
static readonly type = new ElementAttribute( "type", false );
|
|
|
|
static readonly lang = new ElementAttribute( "lang", false );
|
|
|
|
static readonly src = new ElementAttribute( "src", false );
|
|
|
|
static readonly value = new ElementAttribute( "value", false );
|
|
|
|
|
|
|
|
static readonly svgClass = new ElementAttribute( "class", false, "" );
|
|
|
|
static readonly xlinkHref = new ElementAttribute( "xlink:href", false, DOMNameSpaces.XLink );
|
|
|
|
|
|
|
|
static readonly contentEditable = new ElementAttribute( "contenteditable", false );
|
|
|
|
static readonly allowfullscreen = new ElementAttribute( "allowfullscreen", false );
|
|
|
|
static readonly controls = new ElementAttribute( "controls", false );
|
|
|
|
static readonly preload = new ElementAttribute( "preload", false );
|
2025-09-06 11:33:04 +00:00
|
|
|
static readonly playsinline = new ElementAttribute( "playsinline", false );
|
2025-03-08 08:16:54 +00:00
|
|
|
|
|
|
|
static readonly selected = new ElementAttribute( "selected", false );
|
|
|
|
|
|
|
|
static readonly data_lang = new ElementAttribute( "lang" );
|
|
|
|
|
|
|
|
static readonly title = new ElementAttribute( "title", false );
|
|
|
|
static readonly width = new ElementAttribute( "width", false );
|
|
|
|
static readonly height = new ElementAttribute( "height", false );
|
|
|
|
static readonly frameborder = new ElementAttribute( "frameborder", false );
|
|
|
|
static readonly allow = new ElementAttribute( "allow", false );
|
|
|
|
static readonly autoplay = new ElementAttribute( "autoplay", false );
|
|
|
|
static readonly muted = new ElementAttribute( "muted", false );
|
|
|
|
static readonly loop = new ElementAttribute( "loop", false );
|
|
|
|
static readonly readonly = new ElementAttribute( "readonly", false );
|
|
|
|
|
|
|
|
static readonly dataType = new ElementAttribute( "type" );
|
|
|
|
static readonly dataName = new ElementAttribute( "name" );
|
|
|
|
|
|
|
|
static readonly hidden = new ElementAttribute( "hidden", false );
|
|
|
|
|
|
|
|
static readonly rootHref = new ElementAttribute( "root-href" );
|
|
|
|
|
|
|
|
private _name:string;
|
|
|
|
get name(){ return this._name; }
|
|
|
|
|
|
|
|
get fullName()
|
|
|
|
{
|
|
|
|
return this._useDataPrefix ? ( "data-" + this._name ) : this._name;
|
|
|
|
}
|
|
|
|
|
|
|
|
private _useDataPrefix = true;
|
|
|
|
|
|
|
|
private _useNameSpace = false;
|
|
|
|
get usesNameSpace(){ return this._useNameSpace; }
|
|
|
|
|
|
|
|
private _nameSpace:string = null;
|
|
|
|
get nameSpace(){ return this._nameSpace;}
|
|
|
|
|
|
|
|
constructor( name:string, useDataPrefix:boolean = true, nameSpace:string = null )
|
|
|
|
{
|
|
|
|
if ( this._name && this._name.startsWith( "data-" ) && useDataPrefix )
|
|
|
|
{
|
|
|
|
console.warn(
|
|
|
|
`The name of the attribute starts with a 'data-' prefix AND the 'useDataPrefix' at the same time!` );
|
|
|
|
}
|
|
|
|
|
|
|
|
this._name = name;
|
|
|
|
this._useDataPrefix = useDataPrefix;
|
|
|
|
|
|
|
|
if ( nameSpace )
|
|
|
|
{
|
|
|
|
this._useNameSpace = true;
|
|
|
|
this._nameSpace = nameSpace === "" ? null : nameSpace;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
forAll( target:Element|Document, callback:(element:Element)=>any)
|
|
|
|
{
|
|
|
|
let elements = target.querySelectorAll( this.selector );
|
|
|
|
|
|
|
|
for ( let i = 0; i < elements.length; i++ )
|
|
|
|
{
|
|
|
|
callback( elements[ i ] );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
toString()
|
|
|
|
{
|
|
|
|
return this.fullName;
|
|
|
|
}
|
|
|
|
|
|
|
|
queryDoc()
|
|
|
|
{
|
|
|
|
return document.querySelector( this.selector );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
querySelfOrParent( element:Element )
|
|
|
|
{
|
|
|
|
if ( this.in( element ) )
|
|
|
|
{
|
|
|
|
return element;
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.queryParent( element );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
queryParent( element:Element )
|
|
|
|
{
|
|
|
|
let parent = element.parentElement;
|
|
|
|
|
|
|
|
while ( parent )
|
|
|
|
{
|
|
|
|
if ( this.in( parent ) )
|
|
|
|
{
|
|
|
|
return parent;
|
|
|
|
}
|
|
|
|
|
|
|
|
parent = parent.parentElement;
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
queryParentValue( element:Element )
|
|
|
|
{
|
|
|
|
let parent = this.queryParent( element );
|
|
|
|
return this.from( parent );
|
|
|
|
}
|
|
|
|
|
|
|
|
queryWithValue( element:Element|Document, value:string )
|
|
|
|
{
|
|
|
|
return element.querySelector( this.selectorEquals( value ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
queryDocWithValue( value:string )
|
|
|
|
{
|
|
|
|
return this.queryWithValue( document, value );
|
|
|
|
}
|
|
|
|
|
|
|
|
hasParentWithValue( element:Element, value:string )
|
|
|
|
{
|
|
|
|
return this.queryParentValue( element ) === value;
|
|
|
|
}
|
|
|
|
|
|
|
|
query<T=Element>( element:Element|Document )
|
|
|
|
{
|
|
|
|
return element.querySelector( this.selector ) as any as T;
|
|
|
|
}
|
|
|
|
|
|
|
|
isQueriedChecked( element:Element )
|
|
|
|
{
|
|
|
|
let queried = this.query<HTMLInputElement>( element );
|
|
|
|
|
|
|
|
if ( ! queried )
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return queried.checked;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
matches( element:Element, regex:RegExp )
|
|
|
|
{
|
|
|
|
if ( ! this.in( element ) )
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return regex.exec( this.from( element ) ) !== null;
|
|
|
|
}
|
|
|
|
|
|
|
|
queryDocAll()
|
|
|
|
{
|
|
|
|
return DOMEditor.nodeListToArray( document.querySelectorAll( this.selector ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
forEachInDoc( callback:(element:Element)=>void )
|
|
|
|
{
|
|
|
|
this.queryDocAll().forEach( callback );
|
|
|
|
}
|
|
|
|
|
|
|
|
queryAll( t:Element|Document )
|
|
|
|
{
|
|
|
|
if ( ! this._needsWalker( this.selector ) )
|
|
|
|
{
|
|
|
|
return this._queryAllQuerySelector( t );
|
|
|
|
}
|
|
|
|
|
|
|
|
return this._queryAllWalker( t );
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
_needsWalker( selector:string )
|
|
|
|
{
|
|
|
|
if ( "[xlink:href]" == selector )
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
_queryAllQuerySelector( element:Element|Document )
|
|
|
|
{
|
|
|
|
return DOMEditor.nodeListToArray( element.querySelectorAll( this.selector ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
_queryAllWalker( t:Element|Document )
|
|
|
|
{
|
|
|
|
let list:Element[] = [];
|
|
|
|
let walker = new HTMLNodeTreeWalker();
|
|
|
|
let end = walker.iterationEndOf( t );
|
|
|
|
let it:Node = t;
|
|
|
|
|
|
|
|
while ( it !== end )
|
|
|
|
{
|
|
|
|
if ( Node.ELEMENT_NODE != it.nodeType )
|
|
|
|
{
|
|
|
|
it = walker.nextNode( it );
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
let element = it as Element;
|
|
|
|
|
|
|
|
if ( this.in( element ) )
|
|
|
|
{
|
|
|
|
list.push( element );
|
|
|
|
}
|
|
|
|
|
|
|
|
it = walker.nextNode( it );
|
|
|
|
}
|
|
|
|
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|
|
|
|
queryValueInDoc( element:Element )
|
|
|
|
{
|
|
|
|
return this.queryValueIn( element, document );
|
|
|
|
}
|
|
|
|
|
|
|
|
queryValueIn( element:Element, source:Document|Element = undefined )
|
|
|
|
{
|
|
|
|
source = source || element;
|
|
|
|
|
|
|
|
let selector = this.from( element );
|
|
|
|
return source.querySelector( selector );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
get selector()
|
|
|
|
{
|
|
|
|
return `[${this.fullName}]`;
|
|
|
|
}
|
|
|
|
|
|
|
|
selectorContaining( fragment:string )
|
|
|
|
{
|
|
|
|
return `[${this.fullName}*="${fragment}"]`;
|
|
|
|
}
|
|
|
|
|
|
|
|
selectorContainingWord( word:string )
|
|
|
|
{
|
|
|
|
return `[${this.fullName}|="${word}"]`;
|
|
|
|
}
|
|
|
|
|
|
|
|
selectorStartsWith( word:string )
|
|
|
|
{
|
|
|
|
return `[${this.fullName}^="${word}"]`;
|
|
|
|
}
|
|
|
|
|
|
|
|
selectorEquals( word:string )
|
|
|
|
{
|
|
|
|
let selector = `[${this.fullName}="${word}"]`;
|
|
|
|
return selector;
|
|
|
|
}
|
|
|
|
|
|
|
|
is( element:Element, value:string|RegExp )
|
|
|
|
{
|
|
|
|
if ( ! this.in( element ) )
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
let stringValue = this.from( element );
|
|
|
|
|
|
|
|
if ( typeof value === "string" )
|
|
|
|
{
|
|
|
|
return value === stringValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
return value.test( stringValue );
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
in( element:Element )
|
|
|
|
{
|
|
|
|
if ( ! element.attributes )
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( this._useNameSpace )
|
|
|
|
{
|
|
|
|
return element.hasAttributeNS( this._nameSpace, this.fullName );
|
|
|
|
}
|
|
|
|
|
|
|
|
return element.hasAttribute( this.fullName );
|
|
|
|
}
|
|
|
|
|
|
|
|
inAll( elements:Element[] )
|
|
|
|
{
|
|
|
|
if ( elements.length === 0 )
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
for ( let e of elements )
|
|
|
|
{
|
|
|
|
if ( ! this.in( e ) )
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
from( element:Element, alternative:string = null )
|
|
|
|
{
|
|
|
|
if ( ! element )
|
|
|
|
{
|
|
|
|
console.log( "Element is null", this );
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( this._useNameSpace )
|
|
|
|
{
|
|
|
|
return element.getAttributeNS( this._nameSpace, this.fullName );
|
|
|
|
}
|
|
|
|
|
|
|
|
return element.getAttribute( this.fullName ) || alternative;
|
|
|
|
}
|
|
|
|
|
|
|
|
toggleRemove( element:Element, value:boolean )
|
|
|
|
{
|
|
|
|
if ( value )
|
|
|
|
{
|
|
|
|
this.to( element, "" );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.removeFrom( element );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
to( element:Element, value:string|number|boolean = "")
|
|
|
|
{
|
|
|
|
value = value + "";
|
|
|
|
|
|
|
|
if ( ! element )
|
|
|
|
{
|
|
|
|
console.log( "Element is null", this );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( this._useNameSpace )
|
|
|
|
{
|
|
|
|
element.setAttributeNS( this._nameSpace, this.fullName, value );
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
element.setAttribute( this.fullName, value );
|
|
|
|
}
|
|
|
|
|
|
|
|
replaceIn( element:Element, matcher:RegExp, replacement:string )
|
|
|
|
{
|
|
|
|
let value = this.from( element );
|
|
|
|
value = value.replace( matcher, replacement );
|
|
|
|
this.to( element, value )
|
|
|
|
}
|
|
|
|
|
|
|
|
removeFrom( element:Element )
|
|
|
|
{
|
|
|
|
if ( this._useNameSpace )
|
|
|
|
{
|
|
|
|
element.removeAttributeNS( this._nameSpace, this.fullName );
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
element.removeAttribute( this.fullName );
|
|
|
|
}
|
|
|
|
|
|
|
|
copy( source:Element, target:Element )
|
|
|
|
{
|
|
|
|
this.to( target, this.from( source ) );
|
|
|
|
}
|
|
|
|
|
2025-09-06 11:33:04 +00:00
|
|
|
asNumberFrom( element:Element, alternative:number = null )
|
|
|
|
{
|
|
|
|
if ( ! this.in( element ) )
|
|
|
|
{
|
|
|
|
return alternative;
|
|
|
|
}
|
|
|
|
|
|
|
|
let value = this.from( element );
|
|
|
|
|
|
|
|
if ( ! value )
|
|
|
|
{
|
|
|
|
return alternative;
|
|
|
|
}
|
|
|
|
|
|
|
|
let numberValue = parseFloat( value );
|
|
|
|
|
|
|
|
if ( isNaN( numberValue ) )
|
|
|
|
{
|
|
|
|
return alternative;
|
|
|
|
}
|
|
|
|
|
|
|
|
return numberValue;
|
|
|
|
}
|
2025-03-08 08:16:54 +00:00
|
|
|
|
|
|
|
}
|