Website Update 2025

This commit is contained in:
Josef 2025-09-06 13:33:04 +02:00
parent f8edbdd581
commit 185e004839
31 changed files with 1483 additions and 152 deletions

View File

@ -1,12 +1,18 @@
import { nextFrame } from "../animation/nextFrame"; import { nextFrame } from "../animation/nextFrame";
import { OnAnimationFrame } from "../animation/OnAnimationFrame"; import { OnAnimationFrame } from "../animation/OnAnimationFrame";
import { sleep } from "../animation/sleep";
import { DateMath } from "../date/DateMath"; import { DateMath } from "../date/DateMath";
import { ActivityAnalyser } from "../dom/ActivityAnalyser"; import { ActivityAnalyser } from "../dom/ActivityAnalyser";
import { ClassFlag } from "../dom/ClassFlag";
import { Fullscreen } from "../dom/Fullscreen";
import { Insight } from "../dom/Insight"; import { Insight } from "../dom/Insight";
import { LandscapeScale } from "../dom/LandscapeScale"; import { LandscapeScale } from "../dom/LandscapeScale";
import { HashScroll } from "../dom/page-features/features/HashScroll";
import { PageHandler, PageHandlerMode } from "../dom/PageHandler"; import { PageHandler, PageHandlerMode } from "../dom/PageHandler";
import { UserAgentDeviceTypes } from "../dom/UserAgentDeviceType"; import { UserAgentDeviceTypes } from "../dom/UserAgentDeviceType";
import { EventSlot } from "../events/EventSlot";
import { TemplatesManager, TemplatesManagerMode } from "../templates/TemplatesManager"; import { TemplatesManager, TemplatesManagerMode } from "../templates/TemplatesManager";
import { waitAround } from "../tools/sleep";
import { AppPathConverter } from "./AppPathConverter"; import { AppPathConverter } from "./AppPathConverter";
export class AppInitializerData export class AppInitializerData
@ -108,7 +114,7 @@ export class App
this.setLoaded(); this.setLoaded();
} }
setTimeout( ()=>{ this.setLoaded() }, 3000 ) setTimeout( ()=>{ this.setLoaded(); App.appReadyFlag.set( document.body ) }, 3000 )
if ( ! data.allowWWWSubdomain ) if ( ! data.allowWWWSubdomain )
{ {
@ -184,12 +190,21 @@ export class App
this.activityAnalyser.start(); this.activityAnalyser.start();
Fullscreen.setFlagOnChange();
await this.pageHandler.initialize(); await this.pageHandler.initialize();
this.initializePage(); await this.initializePage();
HashScroll.applyOnDocument( this.onAnimationFrame );
await sleep( 300 );
App.appReadyFlag.set( document.body );
} }
static appReadyFlag = new ClassFlag( "app-ready" );
async initializePage() async initializePage()
{ {
while ( ! this.loaded ) while ( ! this.loaded )
@ -198,9 +213,15 @@ export class App
} }
//console.log( "App.initializePage" ); //console.log( "App.initializePage" );
this.insight.grabElements(); // this.insight.grabElements();
this.templatesManager.processChildren( document.body ); this.templatesManager.processChildren( document.body );
this.insight.grabElements();
this.pageHandler.replaceLinks(); this.pageHandler.replaceLinks();
this.pageHandler.resolveRootPaths( document );
return Promise.resolve(); return Promise.resolve();
} }

View File

@ -0,0 +1,63 @@
import { HTMLColor } from "./HTMLColor";
export class ColorStop
{
color:HTMLColor;
stop:number;
static create( color:HTMLColor, stop:number )
{
let cs = new ColorStop();
cs.color = color;
cs.stop = stop;
return cs;
}
getLengthCSS()
{
return `${this.color.toString()} ${this.stop}%`;
}
getDegreesCSS()
{
return `${this.color.toString()} ${this.stop}deg`;
}
static createFrom( colors:HTMLColor[], scale = 1 )
{
let colorStops:ColorStop[] = [];
let normalizer = 100 / ( colors.length -1 );
for ( let i = 0; i < colors.length; i++ )
{
let colorStop = new ColorStop();
colorStop.color = colors[ i ];
colorStop.stop = i * normalizer * scale;
colorStops.push( colorStop );
}
return colorStops;
}
static createEqualSpread( factor:number, ...colors:HTMLColor[] )
{
let normalizer = factor / ( colors.length - 1 );
let colorStops:ColorStop[] = [];
for ( let i = 0; i < colors.length; i++ )
{
let colorStop = new ColorStop();
colorStop.color = colors[ i ];
colorStop.stop = i * normalizer;
colorStops.push( colorStop );
}
return colorStops;
}
}

101
browser/colors/Gradient.ts Normal file
View File

@ -0,0 +1,101 @@
import { ColorStop } from "./ColorStop";
import { HTMLColor } from "./HTMLColor";
export abstract class HTMLGradient
{
colorStops:ColorStop[];
getColorStopsLengthCSS()
{
let colorStopValues = this.colorStops.map( c => c.getLengthCSS() );
return colorStopValues.join( ", " );
}
getColorStopsDegressCSS()
{
let colorStopValues = this.colorStops.map( c => c.getDegreesCSS() );
return colorStopValues.join( ", " );
}
applyToBackground( e:HTMLElement )
{
e.style.background = this.toString();
}
abstract toString():string;
}
export class LinearGradient extends HTMLGradient
{
repeating = false;
position = "180deg";
get type()
{
return this.repeating ? "repeating-linear-gradient" : "linear-gradient";
}
toString()
{
return `${this.type}(${this.position}, ${this.getColorStopsLengthCSS()})` + ( this.repeating ? "" : " no-repeat" );
}
static createVertical( color:HTMLColor, h:number = 10, s:number = 5, l:number = 5 )
{
let hslColor = color.toHSL().clone();
let colorA = hslColor.clone().shift( -h, -s, -l );
let colorB = hslColor.clone().shift( h, s, l );
let lg = new LinearGradient();
lg.colorStops = ColorStop.createEqualSpread( 100, colorA, colorB );
return lg;
}
static createHorizontal( left:HTMLColor, right:HTMLColor, normalizedState:number, normalizedWidth:number )
{
let lg = new LinearGradient();
lg.position = "90deg";
lg.colorStops =
[
ColorStop.create( left, normalizedState - normalizedWidth ),
ColorStop.create( right, normalizedState + normalizedWidth ),
]
return lg;
}
static create( colors:HTMLColor[] )
{
let lg = new LinearGradient();
lg.colorStops = ColorStop.createFrom( colors );
return lg;
}
}
export class RadialGradient extends HTMLGradient
{
repeating = false;
position = "circle at center";
get type()
{
return this.repeating ? "repeating-radial-gradient" : "radial-gradient";
}
toString()
{
return `${this.type}(${this.position}, ${this.getColorStopsLengthCSS()})`;
}
}
export class ConicGradient extends HTMLGradient
{
toString()
{
return `conic-gradient(${this.getColorStopsDegressCSS()})`;
}
}

View File

@ -1,7 +1,23 @@
import { leadingZeros } from "../text/leadingZeros";
import { DateHelper } from "./DateHelper"; import { DateHelper } from "./DateHelper";
export class DateMath export class DateMath
{ {
static getMinutesAndSeconds( allSeconds:number )
{
let minutes = Math.floor( allSeconds / 60 );
let seconds =Math.round( allSeconds - minutes * 60 );
return { minutes, seconds };
}
static formatToMinutesAndSeconds( allSeconds:number )
{
let values = this.getMinutesAndSeconds( allSeconds );
let minutes = isNaN( values.minutes ) ? "--" : leadingZeros( values.minutes, 2 );
let seconds = isNaN( values.seconds ) ? "--" : leadingZeros( values.seconds, 2 );
return `${minutes}:${seconds}`;
}
static sort( dates:Date[] ) static sort( dates:Date[] )
{ {

View File

@ -51,6 +51,11 @@ export class ClassFlag
this._value = value; this._value = value;
} }
static isIn( flag:string, element:Element )
{
return new ClassFlag( flag ).in( element );
}
static fromEnum( value:string, prefix = "", appendix = "" ) static fromEnum( value:string, prefix = "", appendix = "" )
{ {
let flag = value.toLowerCase().replace( "_", "-" ); let flag = value.toLowerCase().replace( "_", "-" );
@ -157,59 +162,27 @@ export class ClassFlag
new ClassFlag( value ).set( element ); new ClassFlag( value ).set( element );
} }
static addClass( element:Element, classString:string ) static addClass( element:Element, classString:string )
{ {
let classAttribute = element.getAttribute( "class" ) || ""; element.classList.add( classString );
let matcher = RegExpUtility.createWordMatcher( classString );
if ( matcher.test( classAttribute ) )
{
return;
}
classAttribute += " " + classString;
ClassFlag.setNormalizedClassAttribute( element, classAttribute );
} }
static removeClass( element:Element, classString:string ) static removeClass( element:Element, classString:string )
{ {
let matcher = RegExpUtility.createWordMatcher( classString ); element.classList.remove( classString );
let classAttribute = element.getAttribute( "class" ) || "";
classAttribute = classAttribute.replace( matcher, "" );
ClassFlag.setNormalizedClassAttribute( element, classAttribute );
} }
static hasClass( element:Element, classString:string ) static hasClass( element:Element, classString:string )
{ {
let matcher = RegExpUtility.createWordMatcher( classString ); return element.classList.contains( classString );
let classAttribute = element.getAttribute( "class" ) || "";
return matcher.test( classAttribute );
} }
static toggleClass( element:Element, className:string ) static toggleClass( element:Element, className:string )
{ {
let flag = ! ClassFlag.hasClass( element, className ); element.classList.toggle( className );
ClassFlag.setClass( element, className, flag );
} }
static setNormalizedClassAttribute( element:Element, classAttribute:string )
{
classAttribute = classAttribute.replace( /\s+/g, " " );
classAttribute = classAttribute.trim();
if ( "" === classAttribute )
{
element.removeAttribute( "class" );
}
else
{
element.setAttribute( "class", classAttribute );
}
}
static setClass( element:Element, classString:string, enable:boolean ) static setClass( element:Element, classString:string, enable:boolean )
{ {

View File

@ -1,3 +1,6 @@
import { HTMLNodeTreeWalker } from "../graphs/HTMLNodeTreeWalker";
import { TreeWalker } from "../graphs/TreeWalker";
export class DOMEditor export class DOMEditor
{ {
static nodeListToArray( list:NodeList ) static nodeListToArray( list:NodeList )
@ -99,6 +102,43 @@ export class DOMEditor
return clones; return clones;
} }
static reverseChildren( element:Element )
{
Array.from( element.childNodes ).reverse().forEach( node => {
element.appendChild( node );
});
}
static getChildrenWidth( parent:Element, callback:( e:Element )=>boolean )
{
HTMLNodeTreeWalker.$.forAll
}
static swapNodes( parent:Element, node1:Node, node2:Node )
{
if ( node1 === node2 )
{
return;
}
let next1 = node1.nextSibling;
let next2 = node2.nextSibling;
if ( next1 === node2 )
{
parent.insertBefore( node2, node1 );
}
else if ( next2 === node1 )
{
parent.insertBefore( node1, node2 );
}
else
{
parent.insertBefore( node1, next2 );
parent.insertBefore( node2, next1 );
}
}
static _cssCreationRegex:RegExp; static _cssCreationRegex:RegExp;
static get cssCreationRegex() static get cssCreationRegex()

View File

@ -96,7 +96,7 @@ export class DOMHitTest
static getRelativeWindowPosition( e:MouseEvent|TouchEvent ) static getRelativeWindowPosition( e:MouseEvent|TouchEvent )
{ {
let position = this.getPointerPosition( e ); let position = this.getPointerPosition( e );
let windowBox = new Box2( new Vector2( 0, 0 ), new Vector2( window.innerWidth, window.innerHeight ) ); let windowBox = Box2.create( new Vector2( 0, 0 ), new Vector2( window.innerWidth, window.innerHeight ) );
position.sub( windowBox.min ); position.sub( windowBox.min );
let size = windowBox.size; let size = windowBox.size;

View File

@ -23,6 +23,7 @@ export class ElementAttribute
static readonly allowfullscreen = new ElementAttribute( "allowfullscreen", false ); static readonly allowfullscreen = new ElementAttribute( "allowfullscreen", false );
static readonly controls = new ElementAttribute( "controls", false ); static readonly controls = new ElementAttribute( "controls", false );
static readonly preload = new ElementAttribute( "preload", false ); static readonly preload = new ElementAttribute( "preload", false );
static readonly playsinline = new ElementAttribute( "playsinline", false );
static readonly selected = new ElementAttribute( "selected", false ); static readonly selected = new ElementAttribute( "selected", false );
@ -408,5 +409,28 @@ export class ElementAttribute
this.to( target, this.from( source ) ); this.to( target, this.from( source ) );
} }
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;
}
} }

View File

@ -95,7 +95,7 @@ export class ElementType<E extends Element>
return this._type; return this._type;
} }
forAll( target:Element|Document, callback:(element:E)=>any) forEach( target:Element|Document, callback:(element:E)=>any)
{ {
let elements = this.queryAll( target ); let elements = this.queryAll( target );
@ -105,9 +105,9 @@ export class ElementType<E extends Element>
} }
} }
forAllInDoc( callback:(element:E)=>any) forEachInDoc( callback:(element:E)=>any)
{ {
this.forAll( document, callback ); this.forEach( document, callback );
} }
queryDoc():E queryDoc():E
@ -120,6 +120,11 @@ export class ElementType<E extends Element>
return element.querySelector( this._rawType ) as E; return element.querySelector( this._rawType ) as E;
} }
selects( element:Element )
{
return element.nodeName.toLowerCase() == this._rawType.toLowerCase();
}
queryAll( element:Document|Element ):E[] queryAll( element:Document|Element ):E[]
{ {
return DOMEditor.nodeListToArray( element.querySelectorAll( this._rawType ) ) as E[]; return DOMEditor.nodeListToArray( element.querySelectorAll( this._rawType ) ) as E[];

View File

@ -44,6 +44,9 @@ export type load = "load";
export type selectstart = "selectstart"; export type selectstart = "selectstart";
export type hashchange = "hashchange";
export type visibilitychange = "visibilitychange";
// export type (\w+).+ // export type (\w+).+
// $1 | // $1 |
export type EventListenerType = export type EventListenerType =
@ -86,7 +89,10 @@ export type EventListenerType =
load | load |
selectstart selectstart |
hashchange |
visibilitychange
; ;
@ -140,6 +146,8 @@ export class Events
static readonly load = "load"; static readonly load = "load";
static readonly selectstart:selectstart = "selectstart"; static readonly selectstart:selectstart = "selectstart";
static readonly hashchange:hashchange = "hashchange";
static readonly visibilitychange:visibilitychange = "visibilitychange";
@ -814,6 +822,33 @@ export class OnChange
} }
} }
export class OnHashChange
{
static add( window:Window, callback:( e:Event )=>void, options:any = undefined )
{
window.addEventListener( Events.hashchange, callback, options );
}
static remove( window:Window, callback:( e:Event )=>void )
{
window.removeEventListener( Events.hashchange, callback );
}
}
export class OnVisibilityChange
{
static add( document:Document, callback:( e:Event )=>void, options:any = undefined )
{
window.addEventListener( Events.visibilitychange, callback, options );
}
static remove( document:Document, callback:( e:Event )=>void )
{
window.removeEventListener( Events.visibilitychange, callback );
}
}
export class OnInput export class OnInput
{ {
static add( element:HTMLElement, callback:( e:InputEvent )=>void, options:any = undefined ) static add( element:HTMLElement, callback:( e:InputEvent )=>void, options:any = undefined )

203
browser/dom/Fullscreen.ts Normal file
View File

@ -0,0 +1,203 @@
import { ClassFlag } from "./ClassFlag";
import { ElementAttribute } from "./ElementAttribute";
import { ElementType } from "./ElementType";
export class Fullscreen
{
static readonly flag = new ClassFlag( "is-fullscreen" );
static _listensToChange = false;
static setFlagOnChange()
{
if ( Fullscreen._listensToChange )
{
return;
}
Fullscreen._listensToChange = true;
document.addEventListener( "fullscreenchange",
() =>
{
Fullscreen.flag.setAs( document.body, Fullscreen.isFullscreen() );
}
);
}
static isFullscreen(): boolean
{
let anyDocument:any = document;
if ( anyDocument.fullscreenElement != null )
{
return true;
}
if ( anyDocument.webkitFullscreenElement != null )
{
return true;
}
if ( anyDocument.mozFullScreenElement != null )
{
return true;
}
if ( anyDocument.msFullscreenElement != null )
{
return true;
}
return false;
}
protected static async enterFullscreen( element:HTMLElement ): Promise<void>
{
let anyElement:any = element;
if ( element.requestFullscreen != null )
{
return element.requestFullscreen();
}
if ( anyElement.webkitRequestFullscreen != null )
{
return anyElement.webkitRequestFullscreen();
}
if ( anyElement.mozRequestFullScreen != null )
{
return anyElement.mozRequestFullScreen();
}
if ( anyElement.msRequestFullscreen != null )
{
return anyElement.msRequestFullscreen();
}
return Promise.reject( new Error( "fullscreen api not supported" ));
}
static async exitFullscreen(): Promise<void>
{
let anyDocument: any = document;
if ( anyDocument.exitFullscreen != null )
{
return anyDocument.exitFullscreen();
}
if ( anyDocument.webkitExitFullscreen != null )
{
return anyDocument.webkitExitFullscreen();
}
if ( anyDocument.mozCancelFullScreen != null )
{
return anyDocument.mozCancelFullScreen();
}
if ( anyDocument.msExitFullscreen != null )
{
return anyDocument.msExitFullscreen();
}
return Promise.reject( new Error( "fullscreen exit not supported" ));
}
static async lockOrientation( orientation:string ): Promise<void>
{
let anyScreen: any = ( screen as any );
let api: any = screen.orientation != null
? screen.orientation
: anyScreen.orientation != null
? anyScreen.orientation
: anyScreen.msOrientation;
if ( api == null || typeof api.lock !== "function" )
{
return;
}
try
{
await api.lock( orientation );
}
catch
{
// fail quietly
}
}
static unlockOrientation(): void
{
let anyScreen: any = ( screen as any );
let api: any = screen.orientation != null
? screen.orientation
: anyScreen.orientation != null
? anyScreen.orientation
: anyScreen.msOrientation;
if ( api == null || typeof api.unlock !== "function" )
{
return;
}
try
{
api.unlock();
}
catch
{
// fail quietly
}
}
static async toggleFullscreen( videoContainer:HTMLElement, handleOrientation:"auto"|"portrait"|"landscape"=null ): Promise<void>
{
let videoElement = ElementType.video.query( videoContainer );
ElementAttribute.playsinline.to( videoContainer );
( videoContainer as any ).playsInline = true;
if ( Fullscreen.isFullscreen() )
{
await Fullscreen.exitFullscreen();
if ( handleOrientation )
{
Fullscreen.unlockOrientation();
}
return Promise.resolve();
}
let target:string = null;
if ( handleOrientation !== null )
{
if ( handleOrientation === "auto" )
{
let vw = videoElement.videoWidth || videoElement.clientWidth || 16;
let vh = videoElement.videoHeight || videoElement.clientHeight || 9;
target = vw >= vh ? "landscape" : "portrait";
}
else
{
target = handleOrientation;
}
}
await Fullscreen.enterFullscreen( videoContainer );
if ( target != null )
{
await Fullscreen.lockOrientation( target );
}
return Promise.resolve();
}
}

View File

@ -26,7 +26,7 @@ export class InsightData
isInsight = false; isInsight = false;
wasInsight = false; wasInsight = false;
viewBox:Box2 = null; viewBox:Box2 = null;
elementBox:Box2 = new Box2(); elementBox:Box2 = Box2.polar( 0 );
changeTime:number = 0; changeTime:number = 0;
} }
@ -142,7 +142,7 @@ export class Insight
); );
//console.log( this._elements ); console.log( this._elements );
} }
update() update()
@ -184,6 +184,8 @@ export class Insight
let viewBox = insightData.viewBox; let viewBox = insightData.viewBox;
// console.log( viewBox );
if ( ! viewBox ) if ( ! viewBox )
{ {
viewBox = this._getViewBox( e ); viewBox = this._getViewBox( e );
@ -192,6 +194,7 @@ export class Insight
let elementBox = insightData.elementBox; let elementBox = insightData.elementBox;
// console.log( elementBox, insightData, e );
Box2.updatefromClientRect( insightData.elementBox, e.getBoundingClientRect() ); Box2.updatefromClientRect( insightData.elementBox, e.getBoundingClientRect() );
@ -219,13 +222,13 @@ export class Insight
} }
let insight = viewBox.intersectsBox( elementBox ); let insight = viewBox.intersectsBox( elementBox );
/*
console.log( // console.log(
"Is in sight", insight, // "Is in sight", insight,
"DOCUMENT VIEW BOX:", viewBox, // "DOCUMENT VIEW BOX:", viewBox,
"ELEMENT BOX:", elementBox, // "ELEMENT BOX:", elementBox,
"ELEMENT", e // "ELEMENT", e
);*/ // );
if ( insight === insightData.isInsight ) if ( insight === insightData.isInsight )
{ {
@ -256,6 +259,7 @@ export class Insight
} }
else else
{ {
let isAbove = elementBox.max.y < viewBox.min.y; let isAbove = elementBox.max.y < viewBox.min.y;
Insight.in_sight.setAs( e, false ); Insight.in_sight.setAs( e, false );
Insight.above_sight.setAs( e, isAbove); Insight.above_sight.setAs( e, isAbove);
@ -365,7 +369,7 @@ export class Insight
let startY = window.innerHeight * relativeStart / 100; let startY = window.innerHeight * relativeStart / 100;
let endY = window.innerHeight * relativeEnd / 100; let endY = window.innerHeight * relativeEnd / 100;
return new Box2( new Vector2( -10000, startY ), new Vector2( 10000, endY ) ); return Box2.create( new Vector2( -10000, startY ), new Vector2( 10000, endY ) );
} }

View File

@ -1,23 +1,26 @@
import { DOMOrientation, DOMOrientationType } from "../dom/DOMOrientation"; import { DOMOrientation, DOMOrientationType } from "../dom/DOMOrientation";
import { EventSlot } from "../events/EventSlot"; import { EventSlot } from "../events/EventSlot";
import { ElementType } from "./ElementType"; import { ElementType } from "./ElementType";
import { UserAgentInfo } from "./UserAgentInfo";
export class LandscapeScale export class LandscapeScale
{ {
private _styleElement:Element; _styleElement:Element;
private _lastWidth:number; _lastWidth:number;
private _lastHeight:number; _lastHeight:number;
private _lastOrientation:DOMOrientationType; _lastOrientation:DOMOrientationType;
private _ultraWideRatio:number = 2.2; _ultraWideRatio:number = 2.2;
private _sw:number; _sw:number;
private _sh:number; _sh:number;
private _swidth:number; _swidth:number;
private _sheight:number; _sheight:number;
private _sx:number; _sx:number;
private _sy:number; _sy:number;
private _spx:number; _spx:number;
_fs:number;
get sw() get sw()
{ {
@ -27,6 +30,7 @@ export class LandscapeScale
onScreenSizeChange = new EventSlot<LandscapeScale>(); onScreenSizeChange = new EventSlot<LandscapeScale>();
onOrientationChange = new EventSlot<LandscapeScale>(); onOrientationChange = new EventSlot<LandscapeScale>();
alerted = false;
update() update()
{ {
@ -58,7 +62,9 @@ export class LandscapeScale
this._lastWidth = newWidth; this._lastWidth = newWidth;
this._lastHeight = newHeight; this._lastHeight = newHeight;
console.log( "Updating size:", this._lastWidth, this._lastHeight );
this._sw = newWidth / 100; this._sw = newWidth / 100;
this._sh = newHeight / 100;
if ( ratio > this._ultraWideRatio ) if ( ratio > this._ultraWideRatio )
{ {
@ -84,6 +90,29 @@ export class LandscapeScale
this._swidth = this._sw * 100; this._swidth = this._sw * 100;
this._sheight = this._sh * 100; this._sheight = this._sh * 100;
this._fs = this._sw / 10;
let isChrome = window.innerWidth != window.visualViewport.width;
let fsValue = this._fs + "px";
let ffOffset = "0px";
console.log( "is chrome:", isChrome, window.innerWidth, window.visualViewport.width );
if ( ! isChrome )
{
fsValue = `calc( ${this._fs}px * var(--firefoxScale ) * 0.01 )`;
ffOffset = "19px";
}
// if ( ! this.alerted )
// {
// this.alerted = true;
// // alert( "is chrome:" + isChrome );
// }
this._styleElement.innerHTML = this._styleElement.innerHTML =
` `
@ -98,6 +127,8 @@ export class LandscapeScale
--spx:${this._spx}px; --spx:${this._spx}px;
--spx-raw:${this._spx}; --spx-raw:${this._spx};
--spx-inv:${1/this._spx}px; --spx-inv:${1/this._spx}px;
--fs:${fsValue};
--firefoxOffset:${ffOffset};
} }
` `

View File

@ -60,6 +60,18 @@ export class PageHandler
private _rootPathResolver = new RootPathResolver(); private _rootPathResolver = new RootPathResolver();
get rootPathResolver(){ return this._rootPathResolver; } get rootPathResolver(){ return this._rootPathResolver; }
private _preloadImagesFlag:boolean = true;
private _maxPreloadingDurationMS:number = 2000;
private static _localHost = "http://localhost:8080";
private static _localNetworkRegexPattern = /^(http:\/\/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}?:XXXX)/
private static _localNetworkRegex = RegExpUtility.createRegExp( this._localNetworkRegexPattern, "XXXX", 8080 + "" );
private _isHTMLMode:boolean = false;
private _fileLocation:string = "";
private _isFileMode:boolean = false;
private _pagesLoaded = false;
setPagesPath( path:string ) setPagesPath( path:string )
{ {
this._pagesPath = path; this._pagesPath = path;
@ -115,15 +127,7 @@ export class PageHandler
} }
private _preloadImagesFlag:boolean = true;
private _maxPreloadingDurationMS:number = 2000;
private static _localHost = "http://localhost:8080";
private static _localNetworkRegexPattern = /^(http:\/\/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}?:XXXX)/
private static _localNetworkRegex = RegExpUtility.createRegExp( this._localNetworkRegexPattern, "XXXX", 8080 + "" );
private _isHTMLMode:boolean = false;
private _fileLocation:string = "";
private _isFileMode:boolean = false;
static setLocalHostPort( port:number ) static setLocalHostPort( port:number )
{ {
@ -167,7 +171,6 @@ export class PageHandler
{ {
let adress = this.trimmedLocation; let adress = this.trimmedLocation;
if ( adress.startsWith( PageHandler._localHost ) ) if ( adress.startsWith( PageHandler._localHost ) )
{ {
return PageHandler._localHost + this._localAdressExtension; return PageHandler._localHost + this._localAdressExtension;
@ -191,7 +194,7 @@ export class PageHandler
if ( adress.startsWith( PageHandler._localHost ) ) if ( adress.startsWith( PageHandler._localHost ) )
{ {
console.log( "Is starting with localhost" ); // console.log( "Is starting with localhost" );
return true; return true;
} }
@ -215,9 +218,6 @@ export class PageHandler
private _pagesLoaded = false;
async initialize() async initialize()
{ {
this._isLoading = true; this._isLoading = true;
@ -330,6 +330,12 @@ export class PageHandler
} }
let pageData = await this._getPageData( next ); let pageData = await this._getPageData( next );
if ( pageData == null )
{
console.log( "No page data found:", page, next );
}
this._currentLanguage = pageData.language; this._currentLanguage = pageData.language;
let elementType = new ElementType( this.pageRootTag ); let elementType = new ElementType( this.pageRootTag );
@ -599,18 +605,18 @@ export class PageHandler
{ {
let trimmedLocation = window.location.href; let trimmedLocation = window.location.href;
let hash = /#.+$/.exec( trimmedLocation ); let hash = /#.*$/.exec( trimmedLocation );
if ( hash ) if ( hash )
{ {
trimmedLocation = trimmedLocation.replace( /#.+$/, "" ); trimmedLocation = trimmedLocation.replace( /#.*$/, "" );
} }
let search = /\?.+$/.exec( trimmedLocation ); let search = /\?.*$/.exec( trimmedLocation );
if ( search ) if ( search )
{ {
trimmedLocation = trimmedLocation.replace( /\?.+$/, "" ); trimmedLocation = trimmedLocation.replace( /\?.*$/, "" );
} }
return trimmedLocation; return trimmedLocation;
@ -620,7 +626,7 @@ export class PageHandler
{ {
let trimmedLocation = window.location.href; let trimmedLocation = window.location.href;
let hash = /#\[.+\]$/.exec( trimmedLocation ); let hash = /#\[.*\]$/.exec( trimmedLocation );
if ( hash ) if ( hash )
{ {
@ -1095,6 +1101,11 @@ export class PageHandler
return; return;
} }
if ( hrefValue.startsWith( "#" ) )
{
return;
}
if ( PageHandler.isFunctionHandleLink( hrefValue ) ) if ( PageHandler.isFunctionHandleLink( hrefValue ) )
{ {
PageHandler.makeFunctionHandleLink( a ); PageHandler.makeFunctionHandleLink( a );
@ -1103,9 +1114,13 @@ export class PageHandler
if ( AttributeValue.target_blank.in( a ) ) if ( AttributeValue.target_blank.in( a ) )
{ {
console.log( "Not processing blank link:", ElementAttribute.href.from( a ), a );
return; return;
} }
else
{
console.log( "Processing blank link:", ElementAttribute.href.from( a ), a );
}
this.makePageHandleLink( a ); this.makePageHandleLink( a );
} }

View File

@ -30,6 +30,24 @@ export class UserAgentInfo
return UserAgentInfo.is( /tablet/i ); return UserAgentInfo.is( /tablet/i );
} }
static get isChrome()
{
let ua = navigator.userAgent;
return ua.toLowerCase().includes( "chrome" ) && ! ua.toLowerCase().includes( "edg" );
}
static get isFirefox()
{
let ua = navigator.userAgent;
return ua.toLowerCase().includes( "firefox" );
}
static get isSafari()
{
let ua = navigator.userAgent;
return ua.toLowerCase().includes( "safari" );
}
static get isTV() static get isTV()
{ {
let isTV = UserAgentInfo.is( /smart\-?tv/i ) || UserAgentInfo.is( /net\-?cast/i ) || let isTV = UserAgentInfo.is( /smart\-?tv/i ) || UserAgentInfo.is( /net\-?cast/i ) ||

View File

@ -0,0 +1,258 @@
import { TimePin } from "../../../animation/TimePin";
import { LinearGradient } from "../../../colors/Gradient";
import { HSLColor } from "../../../colors/HSLColor";
import { DateHelper } from "../../../date/DateHelper";
import { DateMath } from "../../../date/DateMath";
import { TimestampMessage } from "../../../messages/TimestampMessage";
import { RegExpUtility } from "../../../text/RegExpUtitlity";
import { ClassFlag } from "../../ClassFlag";
import { DOMHitTest } from "../../DOMHitTest";
import { DOMOrientation } from "../../DOMOrientation";
import { ElementAttribute } from "../../ElementAttribute";
import { ElementType } from "../../ElementType";
import { OnClick, OnMouseEnter, OnMouseMove, OnPause, OnPlay, OnTimeUpdate, OnTouchDrag, OnTouchEnd, OnTouchStart } from "../../EventListeners";
import { Fullscreen } from "../../Fullscreen";
import { Insight } from "../../Insight";
export class MediaGallery
{
static videoElements:HTMLElement[];
static activeVideoElement:HTMLElement;
static attachedGlobalListeners = false;
static insight:Insight;
static readonly mediaVideo = new ElementType( "media-video" );
static readonly mediaImage = new ElementType( "media-image" );
static readonly activeFlag = new ClassFlag( "active-video" );
static readonly itemIndex = new ElementAttribute( "item-index" );
static readonly container = new ElementType ( "media-container" );
static readonly showPlayButton = new ClassFlag( "show-play-button" );
static readonly fullScreenButton = new ElementType( "fullscreen-button" );
static readonly progressIndicator = new ElementType( "progress-indicator" );
static applyOnDocument( insight:Insight )
{
this.insight = insight;
this.attachGlobalListener();
MediaGallery.videoElements = MediaGallery.mediaVideo.queryAll( document.body ) as HTMLElement[];
MediaGallery.videoElements.forEach( v => this.applyOnVideoElement( v ) );
}
static attachGlobalListener()
{
if ( MediaGallery.attachedGlobalListeners )
{
return;
}
MediaGallery.attachedGlobalListeners = true;
// OnMouseMove.add(
// document.body,
// ( e )=>
// {
// if ( DOMOrientation.isPortrait )
// {
// return;
// }
// if ( ! MediaGallery.activeVideoElement )
// {
// return;
// }
// let isOver = DOMHitTest.isPointerOver( e, MediaGallery.activeVideoElement );
// if ( isOver )
// {
// return;
// }
// MediaGallery.updateActiveVideo( null );
// }
// )
}
static applyOnVideoElement( element:Element )
{
let videoElement = ElementType.video.query( element );
let fs = MediaGallery.fullScreenButton.query( element );
let pi = MediaGallery.progressIndicator.query( element ) as HTMLElement;
OnClick.add( pi,
( e )=>
{
let relative = DOMHitTest.getNormalizedPointerPosition( e, pi );
let position = Math.round( videoElement.duration * relative.x );
videoElement.currentTime = position;
e.preventDefault();
e.stopImmediatePropagation();
e.stopPropagation();
}
);
let updateVideoTimeCode = ( text:string = null )=>
{
let textValue = text != null ? text : ( DateMath.formatToMinutesAndSeconds( videoElement.currentTime ) + " / " +
DateMath.formatToMinutesAndSeconds( videoElement.duration ) );
pi.innerHTML = `<span class="label">${textValue}</span>`;
let percent = text != null ? 0 : (videoElement.currentTime / videoElement.duration) * 100;
let leftColor = new HSLColor( 0, 0, 100, 0.3 );
let rightColor = new HSLColor( 0, 0, 100, 0.1 );
let linearBG = LinearGradient.createHorizontal( leftColor, rightColor, percent, 1 );
pi.style.background = linearBG + ", hsl(0,0%,0%,0.3)";
};
OnTimeUpdate.add( videoElement, () => { updateVideoTimeCode(); } );
OnClick.add( fs as HTMLElement,
()=>
{
Fullscreen.toggleFullscreen( element as HTMLElement, "auto" );
}
);
updateVideoTimeCode( "--:--" );
// OnMouseEnter.add( element as HTMLElement,
// ()=>
// {
// if ( DOMOrientation.isPortrait )
// {
// return;
// }
// MediaGallery.updateActiveVideo( element );
// }
// );
MediaGallery.showPlayButton.setAs( element, true );
OnClick.add( element as HTMLElement,
( me ) =>
{
if ( DOMOrientation.isPortrait )
{
return;
}
if ( videoElement.paused )
{
this.updateActiveVideo( element );
MediaGallery.showPlayButton.setAs( element, false );
}
else
{
videoElement.pause();
MediaGallery.showPlayButton.setAs( element, true );
}
}
);
OnTouchStart.add( element as HTMLElement,
( touchStartEvent )=>
{
if ( touchStartEvent.target == pi || DOMOrientation.isLandscape )
{
return;
}
console.log( touchStartEvent );
let startPosition = DOMHitTest.getRelativeWindowPosition( touchStartEvent );
let moved = false;
let treshold = 0.1;
let onDrag = ( dragEvent:TouchEvent )=>
{
if ( moved )
{
return;
}
let dragPosition = DOMHitTest.getRelativeWindowPosition( dragEvent );
moved = dragPosition.sub( startPosition ).length > treshold;
}
let onEnd = ()=>
{
OnTouchEnd.remove( element as HTMLElement, onEnd );
OnTouchDrag.remove( element as HTMLElement, onDrag );
if ( moved )
{
return;
}
let videoElement = ElementType.video.query( element );
if ( videoElement.paused )
{
this.updateActiveVideo( element );
MediaGallery.showPlayButton.setAs( element, false );
}
else
{
videoElement.pause();
MediaGallery.showPlayButton.setAs( element, true );
}
}
OnTouchEnd.add( element as HTMLElement, onEnd );
OnTouchDrag.add( element as HTMLElement, onDrag );
}
)
}
static updateActiveVideo( e:Element )
{
MediaGallery.activeVideoElement = e as HTMLElement;
this.videoElements.forEach(
( v )=>
{
let isActive = MediaGallery.activeVideoElement === v;
this.activeFlag.setAs( v, isActive );
let video = ElementType.video.query( v );
if ( ! video )
{
return;
}
if ( video.paused === ! isActive )
{
return;
}
if ( isActive )
{
// video.currentTime = 0;
video.play();
}
else
{
video.pause();
MediaGallery.showPlayButton.setAs( v, true );
}
}
)
}
}

View File

@ -0,0 +1,234 @@
import { Vector2 } from "../../../geometry/Vector2";
import { MathX } from "../../../math/MathX";
import { ClassFlag } from "../../ClassFlag";
import { DOMHitTest } from "../../DOMHitTest";
import { DOMOrientation } from "../../DOMOrientation";
import { ElementAttribute } from "../../ElementAttribute";
import { ElementType } from "../../ElementType";
import { OnClick, OnDrag } from "../../EventListeners";
import { MediaGallery } from "./MediaGallery";
export class OffsetElement extends HTMLElement
{
_position:Vector2;
static applyOffset( e:OffsetElement )
{
e.style.transform = `translate(${e._position.x}px, ${e._position.y}px)`;
}
}
export class MediaGallerySelector
{
static type = new ElementType( "media-gallery-selector" );
static target = new ElementAttribute( "target" );
static leftArrow = new ElementAttribute( "left-arrow" );
static rightArrow = new ElementAttribute( "right-arrow" );
static state = new ElementAttribute( "state" );
static active = new ClassFlag( "active" );
static index = new ElementAttribute( "index" );
static applyOnDocument()
{
MediaGallerySelector.type.forEachInDoc( e => this.applyOnElement( e ) );
}
static applyOnElement( e:Element )
{
let target = MediaGallerySelector.target.queryValueInDoc( e );
let items = MediaGallery.itemIndex.queryAll( target );
items.sort(
( a:any, b:any )=>
{
let indexA = MediaGallery.itemIndex.asNumberFrom( a );
let indexB = MediaGallery.itemIndex.asNumberFrom( b );
return indexB - indexA ;
}
);
let offsetElement = target as OffsetElement;
offsetElement._position = new Vector2();
let startPosition = new Vector2();
let direction = 0;
OnDrag.add( offsetElement,
( ev )=>
{
if ( DOMOrientation.isLandscape )
{
return;
}
let position = DOMHitTest.getPointerPosition( ev);
if ( /start|down/.test( ev.type ) )
{
startPosition = position.clone();
direction = 0;
}
else if ( /end|up/.test( ev.type ) )
{
if ( direction == 1 )
{
let index = MediaGallerySelector.index.asNumberFrom( e );
let indexOffset = ( offsetElement._position.x > 0 ? -1 : 1 );
console.log( "offset:", offsetElement._position.x, "index offset:", indexOffset );
index += indexOffset;
let videos = MediaGallery.mediaVideo.queryAll( target );
let images = MediaGallery.mediaImage.queryAll( target );
let num = videos.length + images.length;
index = MathX.clamp( index, 0, num - 1 );
MediaGallerySelector.index.to( e, index + "" );
MediaGallerySelector.redraw( e );
}
offsetElement._position.set( 0, 0 );
OffsetElement.applyOffset( offsetElement );
}
else
{
let diff = position.clone().sub( startPosition );
if ( direction == 0 && diff.length > 5 )
{
direction = Math.abs( diff.x ) > Math.abs( diff.y ) ? 1 : -1;
}
if ( direction == -1 )
{
offsetElement._position.set( 0, 0 );
OffsetElement.applyOffset( offsetElement );
return;
}
diff.multiply( 0.2 );
offsetElement._position.set( diff.x, diff.y );
offsetElement._position.clampPolar( window.innerWidth / 10, 0 );
OffsetElement.applyOffset( offsetElement );
}
if ( direction > 0 )
{
ev.stopImmediatePropagation();
ev.stopPropagation();
ev.preventDefault();
}
}
);
MediaGallerySelector.leftArrow.forAll( e,
l =>
{
OnClick.add( l as HTMLElement,
()=>
{
console.log( "WHI L" );
let index = MediaGallerySelector.index.asNumberFrom( e );
if ( index == 0 )
{
return;
}
index --;
MediaGallerySelector.index.to( e, index + "" );
MediaGallerySelector.redraw( e );
}
);
}
);
MediaGallerySelector.rightArrow.forAll( e,
r =>
{
OnClick.add( r as HTMLElement,
()=>
{
console.log( "WHI R" );
let index = MediaGallerySelector.index.asNumberFrom( e );
if ( index == ( items.length - 1 ) )
{
return;
}
index ++;
MediaGallerySelector.index.to( e, index + "" );
MediaGallerySelector.redraw( e );
}
);
}
);
MediaGallerySelector.state.forAll( e,
s =>
{
OnClick.add( s as HTMLElement,
()=>
{
let stateIndex = MediaGallerySelector.index.asNumberFrom( s );
console.log( "WHI INDEX", stateIndex );
MediaGallerySelector.index.to( e, stateIndex + "" );
MediaGallerySelector.redraw( e );
}
);
}
);
MediaGallerySelector.redraw( e );
}
static redraw( e:Element )
{
let index = MediaGallerySelector.index.asNumberFrom( e );
let target = MediaGallerySelector.target.queryValueInDoc( e );
let container = MediaGallery.container.query( target ) as HTMLElement;
let offset = index * -103;
let value = `translate3d(${offset}vw,0vw,0vw)`;
container.style.transform = value;
console.log( "current-index", index );
let numElements = 3;
MediaGallerySelector.leftArrow.forAll( e,
l => MediaGallerySelector.active.setAs( l, index > 0 )
);
MediaGallerySelector.rightArrow.forAll( e,
r => MediaGallerySelector.active.setAs( r, index < ( numElements - 1 ))
);
MediaGallerySelector.state.forAll( e,
s =>
{
let stateIndex = MediaGallerySelector.index.asNumberFrom( s );
MediaGallerySelector.active.setAs( s, index == stateIndex );
}
);
}
}

View File

@ -0,0 +1,67 @@
import { OnAnimationFrame } from "../../../animation/OnAnimationFrame";
import { OnChange, OnHashChange, OnVisibilityChange } from "../../EventListeners";
export class HashScroll
{
static _hasScrolled = false;
static _hasListener = false;
static applyOnDocument( onAnimationFrame:OnAnimationFrame )
{
HashScroll._hasScrolled = false;
HashScroll.scroll();
if ( this._hasListener )
{
return;
}
OnVisibilityChange.add( document, HashScroll._visibilityHandler );
HashScroll._hasListener = true;
OnHashChange.add( window, ()=> HashScroll.scroll() );
}
static scroll()
{
console.log( "Scrolling:", document.hidden );
let hash = location.hash;
if ( hash )
{
let el = document.querySelector( hash );
if ( el )
{
el.scrollIntoView( { behavior: "smooth", block: "start" } );
}
}
}
static _doScrollOnce()
{
if ( this._hasScrolled )
{
return;
}
HashScroll.scroll();
HashScroll._hasScrolled = true;
document.removeEventListener( "visibilitychange", HashScroll._visibilityHandler );
}
static _visibilityHandler = () =>
{
if ( ! document.hidden )
{
HashScroll._doScrollOnce();
}
}
}

View File

@ -0,0 +1,64 @@
import { ClassFlag } from "../../ClassFlag";
import { ElementAttribute } from "../../ElementAttribute";
import { OnClick } from "../../EventListeners";
export class ToggleClass
{
static readonly attribute = new ElementAttribute( "-toggle--class" );
static readonly assignedAttribute = new ElementAttribute( "-toggle--class--assigned" );
static readonly blockDurationMS = 500;
static applyOnDocument()
{
console.log( "APPLY" );
ToggleClass.attribute.forEachInDoc( ( e ) => ToggleClass.applyOnElement( e ) );
}
static applyOnElement( element:Element )
{
console.log( "APPLY", element );
if ( ToggleClass.assignedAttribute.in( element ) )
{
return;
}
ToggleClass.assignedAttribute.to( element, "0" );
let action = ()=>
{
let now = new Date().getTime();
let time = parseFloat( ToggleClass.assignedAttribute.from( element ) );
let elapsed = now - time;
if ( elapsed < ToggleClass.blockDurationMS )
{
return;
}
ToggleClass.assignedAttribute.to( element, now + "");
let parameters = ToggleClass.attribute.from( element ).split( ";" );
let selector = parameters[ 0 ];
let className = parameters[ 1 ];
let selectedElements = document.querySelectorAll( selector );
selectedElements.forEach(
( selectedElement )=>
{
console.log( selector, selectedElement );
ClassFlag.toggleClass( selectedElement, className );
}
)
}
OnClick.add( element as HTMLElement, action );
}
}

View File

@ -0,0 +1,44 @@
import { ElementAttribute } from "../../ElementAttribute";
import { ElementType } from "../../ElementType";
import { OnMouseDown, OnTouchStart } from "../../EventListeners";
export class VideoAutoPlay
{
static applyOnDocument()
{
let startAutoPlayVideos = ()=>
{
ElementType.video.forEachInDoc(
async ( v )=>
{
if ( ! ElementAttribute.autoplay.in( v ) )
{
return;
}
if ( ! v.paused )
{
return;
}
try
{
await v.play();
}
catch ( e )
{
}
}
);
OnMouseDown.remove( document.body, startAutoPlayVideos );
OnTouchStart.remove( document.body, startAutoPlayVideos );
};
OnMouseDown.add( document.body, startAutoPlayVideos );
OnTouchStart.add( document.body, startAutoPlayVideos );
}
}

View File

@ -0,0 +1,21 @@
import { ElementAttribute } from "../../ElementAttribute";
import { ElementType } from "../../ElementType";
import { OnMouseDown, OnTouchStart } from "../../EventListeners";
export class VideoSpeedAttribute
{
static readonly attribute = new ElementAttribute( "video-speed" );
static applyOnDocument()
{
VideoSpeedAttribute.attribute.forEachInDoc(
( e )=>
{
let video = e as HTMLVideoElement;
let speed = VideoSpeedAttribute.attribute.asNumberFrom( e );
video.playbackRate = speed;
}
);
}
}

View File

@ -6,10 +6,18 @@ export class Box2
min:Vector2; min:Vector2;
max:Vector2; max:Vector2;
constructor( min:Vector2 = null, max:Vector2 = null ) static create( min:Vector2, max:Vector2 ):Box2
{ {
this.min = min; let box = new Box2();
this.max = max; box.min = min;
box.max = max;
return box;
}
static polar( value:number ):Box2
{
return this.create( new Vector2( -value, -value ), new Vector2( value, value ) );
} }
@ -50,7 +58,7 @@ export class Box2
let min = new Vector2( clientRect.left, clientRect.top ); let min = new Vector2( clientRect.left, clientRect.top );
let max = new Vector2( clientRect.right, clientRect.bottom ); let max = new Vector2( clientRect.right, clientRect.bottom );
return new Box2( min, max ); return Box2.create( min, max );
} }

View File

@ -1,3 +1,5 @@
import { MathX } from "../math/MathX";
export class Vector2 export class Vector2
{ {
x:number = 0; x:number = 0;
@ -49,4 +51,10 @@ export class Vector2
return Math.sqrt( this.x * this.x + this.y * this.y ); return Math.sqrt( this.x * this.x + this.y * this.y );
} }
clampPolar( x:number, y:number)
{
this.x = MathX.clamp( this.x, -x, x );
this.y = MathX.clamp( this.y, -y, y );
}
} }

View File

@ -94,6 +94,25 @@ export abstract class TreeWalker<N>
return num <= 0?null:this.childAt( node, num-1 ); return num <= 0?null:this.childAt( node, num-1 );
} }
getAll( node:N, selector:( n:N )=> boolean )
{
let output:N[] = [];
for ( let i = 0; i < this.numChildren( node ); i++ )
{
let n = this.childAt( node, i );
if ( ! selector( n ) )
{
continue;
}
output.push( n );
}
return output;
}
forAll( node:N, callback:( node:N ) => void ) forAll( node:N, callback:( node:N ) => void )
{ {
let end = this.iterationEndOf( node ); let end = this.iterationEndOf( node );

View File

@ -0,0 +1,10 @@
<style>
break--space
{
display: block;
height: 1em;
}
</style>

View File

@ -0,0 +1,10 @@
<style>
column--break,
.column--break
{
break-before: column;
}
</style>

View File

@ -0,0 +1,13 @@
<style>
@media ( orientation: portrait )
{
.landscape--only,
landscape--only
{
display:none;
}
}
</style>

View File

@ -0,0 +1,12 @@
<style>
@media ( orientation: landscape )
{
.portrait--only,
portrait--only
{
display:none;
}
}
</style>

View File

@ -25,7 +25,7 @@ export class StylesProcessorLexer extends Lexer
static readonly ROOT_ELEMENT_REPLACER:ROOT_ELEMENT_REPLACER = "ROOT_ELEMENT_REPLACER"; static readonly ROOT_ELEMENT_REPLACER:ROOT_ELEMENT_REPLACER = "ROOT_ELEMENT_REPLACER";
static readonly ROOT_ELEMENT_REPLACER_MATCHER = static readonly ROOT_ELEMENT_REPLACER_MATCHER =
new LexerMatcher( StylesProcessorLexer.ROOT_ELEMENT_REPLACER, /\[_?\]/ ); new LexerMatcher( StylesProcessorLexer.ROOT_ELEMENT_REPLACER, /#_/ );
constructor() constructor()
{ {
super(); super();

View File

@ -826,6 +826,12 @@ export class RegExpUtility
return new RegExp( source ); return new RegExp( source );
} }
static createClassMatcher( source:string )
{
source = "(^|\\s)" + RegExpUtility.toRegexSource( source ) + "(\\s|$)";
return new RegExp( source );
}
static createMatcher( source:string ) static createMatcher( source:string )
{ {
return new RegExp( RegExpUtility.toRegexSource( source ) ); return new RegExp( RegExpUtility.toRegexSource( source ) );

View File

@ -12,12 +12,12 @@ export class TemplatesInfo
export class TemplatesIndexBuilder export class TemplatesIndexBuilder
{ {
inputDir:string; inputDirectories:string[];
outputDir:string; outputDir:string;
constructor( source:string, build:string ) constructor( source:string[], build:string )
{ {
this.inputDir = source; this.inputDirectories = source;
this.outputDir = build; this.outputDir = build;
} }
@ -37,7 +37,9 @@ export class TemplatesIndexBuilder
compiler.hooks.afterCompile.tapAsync( "TemplatesIndexBuilder", compiler.hooks.afterCompile.tapAsync( "TemplatesIndexBuilder",
async ( compilation:any, callback:any ) => async ( compilation:any, callback:any ) =>
{ {
await Files.forAllIn( this.inputDir, null, for ( let inputDirectory of this.inputDirectories )
{
await Files.forAllIn( inputDirectory, null,
async ( p:PathReference ) => async ( p:PathReference ) =>
{ {
if ( await p.isDirectory() ) if ( await p.isDirectory() )
@ -57,6 +59,7 @@ export class TemplatesIndexBuilder
return Promise.resolve(); return Promise.resolve();
} }
); );
}
callback(); callback();
@ -67,7 +70,10 @@ export class TemplatesIndexBuilder
async ( compilation:any, callback:any ) => async ( compilation:any, callback:any ) =>
{ {
let templates = new TemplatesInfo(); let templates = new TemplatesInfo();
let templatesPathReference = new PathReference( path.resolve( this.inputDir ) );
for ( let inputDirectory of this.inputDirectories )
{
let templatesPathReference = new PathReference( path.resolve( inputDirectory ) );
// RJLog.log( templatesPathReference.absolutePath ); // RJLog.log( templatesPathReference.absolutePath );
@ -108,6 +114,8 @@ export class TemplatesIndexBuilder
} }
); );
}
let templatesPath = path.join( this.outputDir, "index.json" ); let templatesPath = path.join( this.outputDir, "index.json" );
let templatesJSON = JSON.stringify( templates, null, " " ); let templatesJSON = JSON.stringify( templates, null, " " );