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 ); 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( element:Element|Document ) { return element.querySelector( this.selector ) as any as T; } isQueriedChecked( element:Element ) { let queried = this.query( 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 ) ); } }