Page Handler Update
This commit is contained in:
parent
7a05d26162
commit
1238a1ff7c
|
@ -0,0 +1,212 @@
|
||||||
|
import { nextFrame } from "../animation/nextFrame";
|
||||||
|
import { OnAnimationFrame } from "../animation/OnAnimationFrame";
|
||||||
|
import { DateMath } from "../date/DateMath";
|
||||||
|
import { ActivityAnalyser } from "../dom/ActivityAnalyser";
|
||||||
|
import { Insight } from "../dom/Insight";
|
||||||
|
import { LandscapeScale } from "../dom/LandscapeScale";
|
||||||
|
import { PageHandler, PageHandlerMode } from "../dom/PageHandler";
|
||||||
|
import { UserAgentDeviceTypes } from "../dom/UserAgentDeviceType";
|
||||||
|
import { TemplatesManager, TemplatesManagerMode } from "../templates/TemplatesManager";
|
||||||
|
import { AppPathConverter } from "./AppPathConverter";
|
||||||
|
|
||||||
|
export class AppInitializerData
|
||||||
|
{
|
||||||
|
localAdress:string;
|
||||||
|
webAdress:string;
|
||||||
|
templatesList:string[];
|
||||||
|
addTemplateStyles:boolean = false;
|
||||||
|
pageRoot:string;
|
||||||
|
pagesPath?:string;
|
||||||
|
disableForceHTTPS?:boolean;
|
||||||
|
htmlMode?:boolean;
|
||||||
|
allowWWWSubdomain?:boolean;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ElectronAppInitializerData
|
||||||
|
{
|
||||||
|
appLocation:string;
|
||||||
|
templatesList:string[];
|
||||||
|
pageRoot:string;
|
||||||
|
disableForceHTTPS?:boolean;
|
||||||
|
htmlMode?:boolean;
|
||||||
|
allowWWWSubdomain:boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class App
|
||||||
|
{
|
||||||
|
readonly activityAnalyser = new ActivityAnalyser();
|
||||||
|
readonly onAnimationFrame = new OnAnimationFrame();
|
||||||
|
readonly templatesManager = new TemplatesManager();
|
||||||
|
readonly insight = new Insight();
|
||||||
|
readonly landscapeScale = new LandscapeScale();
|
||||||
|
readonly pathConverter = new AppPathConverter( this );
|
||||||
|
|
||||||
|
private _pageHandler:PageHandler;
|
||||||
|
private _initializerData:AppInitializerData;
|
||||||
|
private _electronInitializerData:ElectronAppInitializerData;
|
||||||
|
|
||||||
|
protected _loaded = false;
|
||||||
|
get loaded()
|
||||||
|
{
|
||||||
|
return this._loaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected _loadedTime:Date = null;
|
||||||
|
|
||||||
|
get timeElapsedSinceLoaded()
|
||||||
|
{
|
||||||
|
return DateMath.getDifferenceMs( new Date(), this._loadedTime ) / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoaded()
|
||||||
|
{
|
||||||
|
this._loaded = true;
|
||||||
|
this._loadedTime = new Date();
|
||||||
|
}
|
||||||
|
|
||||||
|
get initializerData(){ return this._initializerData; }
|
||||||
|
|
||||||
|
get pageHandler(){ return this._pageHandler; }
|
||||||
|
|
||||||
|
|
||||||
|
get webAdress()
|
||||||
|
{
|
||||||
|
return this._initializerData.webAdress;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isHTMLMode()
|
||||||
|
{
|
||||||
|
return this._initializerData.htmlMode === true;
|
||||||
|
}
|
||||||
|
|
||||||
|
grabHash()
|
||||||
|
{
|
||||||
|
let hash = window.location.hash;
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isLocal()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async initializeApp( data:AppInitializerData|ElectronAppInitializerData ):Promise<void>
|
||||||
|
{
|
||||||
|
if ( document.readyState === "loading" )
|
||||||
|
{
|
||||||
|
document.addEventListener(
|
||||||
|
"DOMContentLoaded",
|
||||||
|
()=>
|
||||||
|
{
|
||||||
|
this.setLoaded();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.setLoaded();
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout( ()=>{ this.setLoaded() }, 3000 )
|
||||||
|
|
||||||
|
if ( ! data.allowWWWSubdomain )
|
||||||
|
{
|
||||||
|
let url = window.location + "";
|
||||||
|
|
||||||
|
if ( url.startsWith( "https://www." ) )
|
||||||
|
{
|
||||||
|
let realURL = url.replace( /^https:\/\/www\./, "https://" );
|
||||||
|
|
||||||
|
window.location.assign( realURL );
|
||||||
|
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UserAgentDeviceTypes.setOnBody();
|
||||||
|
|
||||||
|
if ( ( data as ElectronAppInitializerData ).appLocation )
|
||||||
|
{
|
||||||
|
console.log( "ELECTRON APP" );
|
||||||
|
this._electronInitializerData = data as ElectronAppInitializerData;
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this._initializerData = data as AppInitializerData;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if ( data.templatesList )
|
||||||
|
{
|
||||||
|
let templatesList = data.templatesList;
|
||||||
|
let templateStylesMode = this.initializerData.addTemplateStyles ?
|
||||||
|
TemplatesManagerMode.ADD_STYLES_TO_HEAD :
|
||||||
|
TemplatesManagerMode.IGNORE_STYLES;
|
||||||
|
|
||||||
|
this.templatesManager.setMode( templateStylesMode );
|
||||||
|
templatesList.forEach( c => this.templatesManager.addTemplateHTML( c ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( this._initializerData )
|
||||||
|
{
|
||||||
|
//console.log( "PAGE HANDLER FOR WEB" );
|
||||||
|
let appData = this._initializerData;
|
||||||
|
this._pageHandler = new PageHandler( appData.localAdress, appData.webAdress, data.disableForceHTTPS );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//console.log( "PAGE HANDLER FOR APP" );
|
||||||
|
let electronData = this._electronInitializerData;
|
||||||
|
this._pageHandler = new PageHandler( electronData.appLocation, null, electronData.disableForceHTTPS, PageHandlerMode.ELECTRON );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
this.pageHandler.setPageRootTag( data.pageRoot );
|
||||||
|
|
||||||
|
if ( this._initializerData.pagesPath )
|
||||||
|
{
|
||||||
|
this.pageHandler.setPagesPath( this._initializerData.pagesPath );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
this.onAnimationFrame.run();
|
||||||
|
|
||||||
|
this.onAnimationFrame.addListener(
|
||||||
|
()=>
|
||||||
|
{
|
||||||
|
this.landscapeScale.update();
|
||||||
|
this.insight.update();
|
||||||
|
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
this.activityAnalyser.start();
|
||||||
|
|
||||||
|
await this.pageHandler.initialize();
|
||||||
|
|
||||||
|
this.initializePage();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async initializePage()
|
||||||
|
{
|
||||||
|
while ( ! this.loaded )
|
||||||
|
{
|
||||||
|
await nextFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
//console.log( "App.initializePage" );
|
||||||
|
this.insight.grabElements();
|
||||||
|
this.templatesManager.processChildren( document.body );
|
||||||
|
this.pageHandler.replaceLinks();
|
||||||
|
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
static updateAll()
|
||||||
|
{
|
||||||
|
Insight.updateAll();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
|
||||||
|
import { RootPathResolver } from "../dom/RootPathResolver";
|
||||||
|
import { App } from "./App";
|
||||||
|
|
||||||
|
export class AppPathConverter
|
||||||
|
{
|
||||||
|
#app:App;
|
||||||
|
|
||||||
|
// root: relative to root => ::/en/store
|
||||||
|
// absolute: internet/localhost => https://rokojori.com/en/store || localhost/en/store
|
||||||
|
// relative: to current page => ../en/store
|
||||||
|
|
||||||
|
constructor( app:App )
|
||||||
|
{
|
||||||
|
this.#app = app;
|
||||||
|
}
|
||||||
|
|
||||||
|
rootToRelative( rootPath:string )
|
||||||
|
{
|
||||||
|
// ::/en/store/
|
||||||
|
// -> ../../en/store
|
||||||
|
|
||||||
|
let page = this.#app.pageHandler.currentPage;
|
||||||
|
let rootToken = RootPathResolver.rootToken;
|
||||||
|
let pathToRoot = this.#app.pageHandler.rootPathResolver.getRootPath( page );
|
||||||
|
|
||||||
|
console.log( "PathToRoot", page, pathToRoot );
|
||||||
|
|
||||||
|
let value = rootPath;
|
||||||
|
value = value.replace( rootToken, pathToRoot );
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
export class DateHelper
|
||||||
|
{
|
||||||
|
static now()
|
||||||
|
{
|
||||||
|
let date = new Date();
|
||||||
|
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
|
||||||
|
static today()
|
||||||
|
{
|
||||||
|
let date = new Date();
|
||||||
|
date.setHours( 0, 0, 0 );
|
||||||
|
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
|
||||||
|
static createYMD( year:number, month:number = 1, day:number = 1, hours:number = 0, minutes:number = 0, seconds:number = 0 )
|
||||||
|
{
|
||||||
|
let date = new Date();
|
||||||
|
|
||||||
|
date.setFullYear( year );
|
||||||
|
date.setMonth( month - 1 );
|
||||||
|
date.setDate( day );
|
||||||
|
date.setHours( hours, minutes, seconds );
|
||||||
|
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,154 @@
|
||||||
|
import { DateHelper } from "./DateHelper";
|
||||||
|
|
||||||
|
export class DateMath
|
||||||
|
{
|
||||||
|
|
||||||
|
static sort( dates:Date[] )
|
||||||
|
{
|
||||||
|
dates.sort( DateMath.sortDate );
|
||||||
|
}
|
||||||
|
|
||||||
|
static sortDate( a:Date, b:Date )
|
||||||
|
{
|
||||||
|
return DateMath.isBefore( a, b ) ? -1 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static isNowOlderThanMS( d:Date, ms:number )
|
||||||
|
{
|
||||||
|
let difference = this.getDifferenceMs( DateHelper.now(), d );
|
||||||
|
return difference > ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
static isBeforeNow( d:Date )
|
||||||
|
{
|
||||||
|
return d.getTime() < DateHelper.now().getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
static isInThePast( d:Date )
|
||||||
|
{
|
||||||
|
return DateMath.isBeforeNow( d );
|
||||||
|
}
|
||||||
|
|
||||||
|
static isExpired( d:Date )
|
||||||
|
{
|
||||||
|
return DateMath.isInThePast( d );
|
||||||
|
}
|
||||||
|
|
||||||
|
static isExpiredMS( dateMS:number, durationMS:number )
|
||||||
|
{
|
||||||
|
let end = dateMS + durationMS;
|
||||||
|
|
||||||
|
return end <= DateHelper.now().getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
static isBefore( a:Date, b:Date )
|
||||||
|
{
|
||||||
|
return a.getTime() < b.getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
static isBeforeOrEquals( a:Date, b:Date )
|
||||||
|
{
|
||||||
|
return a.getTime() <= b.getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
static isAfter( a:Date, b:Date )
|
||||||
|
{
|
||||||
|
return a.getTime() > b.getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
static isAfterNow( d:Date )
|
||||||
|
{
|
||||||
|
return d.getTime() > DateHelper.now().getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
static isInTheFuture( d:Date )
|
||||||
|
{
|
||||||
|
return DateMath.isAfterNow( d );
|
||||||
|
}
|
||||||
|
|
||||||
|
static getDifferenceMs( a:Date, b:Date )
|
||||||
|
{
|
||||||
|
return a.getTime() - b.getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
static isAfterOrEquals( a:Date, b:Date )
|
||||||
|
{
|
||||||
|
return a.getTime() >= b.getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
static addMilliseconds( a:Date, durationMS:number )
|
||||||
|
{
|
||||||
|
let dateMS = a.getTime();
|
||||||
|
let milliseconds = dateMS + durationMS;
|
||||||
|
|
||||||
|
return new Date( milliseconds );
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromNowAddDays( days:number )
|
||||||
|
{
|
||||||
|
return DateMath.addDays( new Date(), days );
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromNowAddHours( hours:number )
|
||||||
|
{
|
||||||
|
return DateMath.addHours( new Date(), hours );
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromNowAddMinutes( minutes:number )
|
||||||
|
{
|
||||||
|
return DateMath.addMinutes( new Date(), minutes );
|
||||||
|
}
|
||||||
|
|
||||||
|
static addSeconds( a:Date, duration:number )
|
||||||
|
{
|
||||||
|
return this.addMilliseconds( a, duration * 1000 );
|
||||||
|
}
|
||||||
|
|
||||||
|
static addMinutes( a:Date, durationMinutes:number )
|
||||||
|
{
|
||||||
|
return this.addSeconds( a, durationMinutes * 60 );
|
||||||
|
}
|
||||||
|
|
||||||
|
static addHours( a:Date, durationHours:number )
|
||||||
|
{
|
||||||
|
return this.addMinutes( a, durationHours * 60 );
|
||||||
|
}
|
||||||
|
|
||||||
|
static addDays( a:Date, durationDays:number )
|
||||||
|
{
|
||||||
|
return this.addHours( a, durationDays * 24 );
|
||||||
|
}
|
||||||
|
|
||||||
|
static nextMonth( date:Date )
|
||||||
|
{
|
||||||
|
date = new Date( date );
|
||||||
|
|
||||||
|
if ( date.getMonth() == 11 )
|
||||||
|
{
|
||||||
|
date.setFullYear( date.getFullYear() + 1, 0 );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
date.setMonth( date.getMonth() + 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
|
||||||
|
static isOnSameDay( a:Date, b:Date )
|
||||||
|
{
|
||||||
|
if ( a.getFullYear() !== b.getFullYear() )
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( a.getMonth() !== b.getMonth() )
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return a.getDate() === b.getDate();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
import { EventSlot } from "../events/EventSlot";
|
||||||
|
import { OnBlur, OnFocus, OnMouseDown, OnMouseLeave, OnMouseMove, OnMouseUp, OnTouchCancel, OnTouchEnd, OnTouchMove, OnTouchStart } from "../dom/EventListeners";
|
||||||
|
|
||||||
|
export class ActivityAnalyser
|
||||||
|
{
|
||||||
|
readonly onActive = new EventSlot();
|
||||||
|
readonly onPassive = new EventSlot();
|
||||||
|
private _timeOutDurationMS = 3000;
|
||||||
|
private _active = false;
|
||||||
|
get active(){ return this._active; }
|
||||||
|
|
||||||
|
private _timeOutCallback:any = null;
|
||||||
|
|
||||||
|
start()
|
||||||
|
{
|
||||||
|
let setActive = ()=>
|
||||||
|
{
|
||||||
|
this._cancelPassiveState();
|
||||||
|
|
||||||
|
if ( ! this._active )
|
||||||
|
{
|
||||||
|
this._active = true;
|
||||||
|
this.onActive.dispatch( null );
|
||||||
|
}
|
||||||
|
|
||||||
|
this._reschedulePassiveState();
|
||||||
|
}
|
||||||
|
|
||||||
|
let activeEvents =
|
||||||
|
[
|
||||||
|
OnFocus, OnBlur,
|
||||||
|
OnMouseMove, OnMouseDown, OnMouseUp, OnMouseLeave,
|
||||||
|
OnTouchStart, OnTouchMove, OnTouchCancel, OnTouchEnd
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
activeEvents.forEach(
|
||||||
|
( onEvent )=>
|
||||||
|
{
|
||||||
|
onEvent.add( document.body, setActive );
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private _cancelPassiveState()
|
||||||
|
{
|
||||||
|
if ( this._timeOutCallback )
|
||||||
|
{
|
||||||
|
clearTimeout( this._timeOutCallback );
|
||||||
|
this._timeOutCallback = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _reschedulePassiveState()
|
||||||
|
{
|
||||||
|
this._cancelPassiveState();
|
||||||
|
|
||||||
|
this._timeOutCallback = setTimeout( ()=>{ this._setPassive(); }, this._timeOutDurationMS );
|
||||||
|
}
|
||||||
|
|
||||||
|
private _setPassive()
|
||||||
|
{
|
||||||
|
if ( ! this._active )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._active = false;
|
||||||
|
this.onPassive.dispatch( null );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,161 @@
|
||||||
|
import { DOMEditor } from "./DOMEditor";
|
||||||
|
import { ElementAttribute } from "./ElementAttribute";
|
||||||
|
|
||||||
|
export enum AttributeValueMatchMode
|
||||||
|
{
|
||||||
|
EXACTLY,
|
||||||
|
STARTS_WITH
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AttributeValue
|
||||||
|
{
|
||||||
|
private _value:string;
|
||||||
|
private _attribute:ElementAttribute;
|
||||||
|
private _mode:AttributeValueMatchMode = AttributeValueMatchMode.EXACTLY;
|
||||||
|
|
||||||
|
|
||||||
|
static get type_checkbox(){ return new AttributeValue( ElementAttribute.type, "checkbox" ); }
|
||||||
|
static get type_email(){ return new AttributeValue( ElementAttribute.type, "email" ); }
|
||||||
|
static get type_text(){ return new AttributeValue( ElementAttribute.type, "text" ); }
|
||||||
|
static get type_file(){ return new AttributeValue( ElementAttribute.type, "file" ); }
|
||||||
|
static get type_number(){ return new AttributeValue( ElementAttribute.type, "number" ); }
|
||||||
|
static get type_time(){ return new AttributeValue( ElementAttribute.type, "time" ); }
|
||||||
|
static get type_date(){ return new AttributeValue( ElementAttribute.type, "date" ); }
|
||||||
|
static get type_password(){ return new AttributeValue( ElementAttribute.type, "password" ); }
|
||||||
|
|
||||||
|
static get target_blank(){ return new AttributeValue( ElementAttribute.target, "_blank" ); }
|
||||||
|
|
||||||
|
static get preloadAuto() { return new AttributeValue( ElementAttribute.preload, "auto" ); }
|
||||||
|
static get preloadNone() { return new AttributeValue( ElementAttribute.preload, "none" ); }
|
||||||
|
static get preloadMetaData() { return new AttributeValue( ElementAttribute.preload, "metadata" ); }
|
||||||
|
|
||||||
|
static parseAttributeValue( source:string )
|
||||||
|
{
|
||||||
|
let seperator = source.indexOf( "=" );
|
||||||
|
|
||||||
|
let attributeName = source.substring( 0, seperator ).trim();
|
||||||
|
|
||||||
|
let value = source.substring( seperator + 1 ).trim();
|
||||||
|
|
||||||
|
if ( value.startsWith( "\"" ) || value.startsWith( "'") )
|
||||||
|
{
|
||||||
|
value = value.substring( 1, value.length - 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let attribute = new ElementAttribute( attributeName, false );
|
||||||
|
|
||||||
|
return new AttributeValue( attribute, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor( attribute:ElementAttribute, value:string, mode:AttributeValueMatchMode = AttributeValueMatchMode.EXACTLY )
|
||||||
|
{
|
||||||
|
this._value = value;
|
||||||
|
this._attribute = attribute;
|
||||||
|
this._mode = mode || AttributeValueMatchMode.EXACTLY;
|
||||||
|
}
|
||||||
|
|
||||||
|
get attribute()
|
||||||
|
{
|
||||||
|
return this._attribute;
|
||||||
|
}
|
||||||
|
|
||||||
|
get value()
|
||||||
|
{
|
||||||
|
return this._value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get selector():string
|
||||||
|
{
|
||||||
|
switch ( this._mode )
|
||||||
|
{
|
||||||
|
case AttributeValueMatchMode.EXACTLY:
|
||||||
|
{
|
||||||
|
return this._attribute.selectorEquals( this._value );
|
||||||
|
}
|
||||||
|
|
||||||
|
case AttributeValueMatchMode.STARTS_WITH:
|
||||||
|
{
|
||||||
|
return this._attribute.selectorStartsWith( this._value );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log( "No selector" );
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
find( elements:Element[] )
|
||||||
|
{
|
||||||
|
for ( let i = 0; i < elements.length; i++ )
|
||||||
|
{
|
||||||
|
if ( this.in( elements[ i ] ) )
|
||||||
|
{
|
||||||
|
return elements[ i ];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
query<T extends Element>( element:Element )
|
||||||
|
{
|
||||||
|
return element.querySelector( this.selector ) as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
queryDoc<T extends Element>()
|
||||||
|
{
|
||||||
|
return document.querySelector( this.selector ) as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
queryAll( element:Element )
|
||||||
|
{
|
||||||
|
return DOMEditor.nodeListToArray( element.querySelectorAll( this.selector ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
queryAllInDoc()
|
||||||
|
{
|
||||||
|
return DOMEditor.nodeListToArray( document.querySelectorAll( this.selector ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
set( element:Element )
|
||||||
|
{
|
||||||
|
this._attribute.to( element, this._value );
|
||||||
|
}
|
||||||
|
|
||||||
|
removeFrom( element:Element )
|
||||||
|
{
|
||||||
|
this._attribute.removeFrom( element );
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle( element:Element )
|
||||||
|
{
|
||||||
|
if ( this.in( element ) )
|
||||||
|
{
|
||||||
|
this.removeFrom( element );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.set( element );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isSelected( selectElement:HTMLSelectElement )
|
||||||
|
{
|
||||||
|
return selectElement.value == this.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
in( element:Element )
|
||||||
|
{
|
||||||
|
let attValue = this._attribute.from( element );
|
||||||
|
|
||||||
|
switch ( this._mode )
|
||||||
|
{
|
||||||
|
case AttributeValueMatchMode.EXACTLY: return attValue === this._value;
|
||||||
|
case AttributeValueMatchMode.STARTS_WITH: return attValue.startsWith( this._value );
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
export class Cursor
|
||||||
|
{
|
||||||
|
static lock( type:string = null )
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static free()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,138 @@
|
||||||
|
import { Box2 } from "../geometry/Box2";
|
||||||
|
import { Vector2 } from "../geometry/Vector2";
|
||||||
|
|
||||||
|
export class DOMHitTest
|
||||||
|
{
|
||||||
|
static get scrollOffset():Vector2
|
||||||
|
{
|
||||||
|
return new Vector2( window.scrollX, window.scrollY );
|
||||||
|
}
|
||||||
|
|
||||||
|
static getPageRect( e:Element ):ClientRect
|
||||||
|
{
|
||||||
|
let pageBox = this.getPageBox( e );
|
||||||
|
|
||||||
|
return Box2.toDomRect( pageBox );
|
||||||
|
}
|
||||||
|
|
||||||
|
static other()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static isPointerOver( me:MouseEvent|TouchEvent, e:Element )
|
||||||
|
{
|
||||||
|
if ( ! me || ! e )
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let relative = DOMHitTest.getNormalizedPointerPosition( me, e );
|
||||||
|
|
||||||
|
if ( relative.x < 0 || relative.x > 1)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( relative.y < 0 || relative.y > 1 )
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static getPageBox( e:Element ):Box2
|
||||||
|
{
|
||||||
|
let pageBox = Box2.fromClientRect( e.getBoundingClientRect() );
|
||||||
|
|
||||||
|
pageBox.translate( this.scrollOffset );
|
||||||
|
|
||||||
|
return pageBox;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static _lastPageX:number = null;
|
||||||
|
private static _lastPageY:number = null;
|
||||||
|
|
||||||
|
static getPointerPosition( e:MouseEvent|TouchEvent ):Vector2
|
||||||
|
{
|
||||||
|
let isMouseEvent = /mouse|click/.test( e.type );
|
||||||
|
console.log( e.type );
|
||||||
|
let mouseEvent = isMouseEvent ? e as MouseEvent : null;
|
||||||
|
let touchEvent = isMouseEvent ? null : ( e as TouchEvent );
|
||||||
|
let pageX = isMouseEvent ? mouseEvent.pageX : touchEvent.touches.length === 0 ? null : touchEvent.touches[ 0 ].pageX;
|
||||||
|
let pageY = isMouseEvent ? mouseEvent.pageY : touchEvent.touches.length === 0 ? null : touchEvent.touches[ 0 ].pageY;
|
||||||
|
|
||||||
|
// if ( isMouseEvent && CustomMouse.active )
|
||||||
|
// {
|
||||||
|
// pageX = CustomMouse.x;
|
||||||
|
// pageY = CustomMouse.y;
|
||||||
|
// }
|
||||||
|
|
||||||
|
if ( pageX === null )
|
||||||
|
{
|
||||||
|
pageX = DOMHitTest._lastPageX === undefined ? window.innerWidth / 2 : DOMHitTest._lastPageX;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( pageY === null )
|
||||||
|
{
|
||||||
|
pageY = DOMHitTest._lastPageY === undefined ? window.innerHeight / 2 : DOMHitTest._lastPageY;
|
||||||
|
}
|
||||||
|
|
||||||
|
DOMHitTest._lastPageX = pageX;
|
||||||
|
DOMHitTest._lastPageY = pageY;
|
||||||
|
|
||||||
|
return new Vector2( pageX, pageY );
|
||||||
|
}
|
||||||
|
|
||||||
|
static getRelativePointerPosition( e:MouseEvent|TouchEvent, element:Element )
|
||||||
|
{
|
||||||
|
let position = this.getPointerPosition( e );
|
||||||
|
let pageRect = this.getPageBox( element );
|
||||||
|
|
||||||
|
return position.sub( pageRect.min );
|
||||||
|
}
|
||||||
|
|
||||||
|
static getRelativeWindowPosition( e:MouseEvent|TouchEvent )
|
||||||
|
{
|
||||||
|
let position = this.getPointerPosition( e );
|
||||||
|
let windowBox = new Box2( new Vector2( 0, 0 ), new Vector2( window.innerWidth, window.innerHeight ) );
|
||||||
|
|
||||||
|
position.sub( windowBox.min );
|
||||||
|
let size = windowBox.size;
|
||||||
|
position.x /= size.x;
|
||||||
|
position.y /= size.y;
|
||||||
|
|
||||||
|
return position;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static getNormalizedPointerPosition( e:MouseEvent|TouchEvent, element:Element )
|
||||||
|
{
|
||||||
|
let position = this.getPointerPosition( e );
|
||||||
|
let pageRect = this.getPageBox( element );
|
||||||
|
|
||||||
|
position.sub( pageRect.min );
|
||||||
|
let size = pageRect.size;
|
||||||
|
position.x /= size.x;
|
||||||
|
position.y /= size.y;
|
||||||
|
|
||||||
|
return position;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static getNormalizedPosition( position:Vector2, element:Element )
|
||||||
|
{
|
||||||
|
let pageRect = this.getPageBox( element );
|
||||||
|
|
||||||
|
position.sub( pageRect.min );
|
||||||
|
let size = pageRect.size;
|
||||||
|
position.x /= size.x;
|
||||||
|
position.y /= size.y;
|
||||||
|
|
||||||
|
return position;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
export enum DOMOrientationType
|
||||||
|
{
|
||||||
|
Portrait,
|
||||||
|
Landscape
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DOMOrientation
|
||||||
|
{
|
||||||
|
static get type()
|
||||||
|
{
|
||||||
|
return this.isLandscape ? DOMOrientationType.Landscape : DOMOrientationType.Portrait;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get isLandscape()
|
||||||
|
{
|
||||||
|
return window.innerWidth > window.innerHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get isPortrait()
|
||||||
|
{
|
||||||
|
return window.innerWidth < window.innerHeight;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,932 @@
|
||||||
|
import { Func } from "../tools/TypeUtilities";
|
||||||
|
import { Cursor } from "./Cursor";
|
||||||
|
import { DOMHitTest } from "./DOMHitTest";
|
||||||
|
|
||||||
|
export type click = "click";
|
||||||
|
|
||||||
|
export type contextmenu = "contextmenu";
|
||||||
|
|
||||||
|
export type touchstart = "touchstart";
|
||||||
|
export type touchmove = "touchmove";
|
||||||
|
export type touchcancel = "touchcancel";
|
||||||
|
export type touchend = "touchend";
|
||||||
|
|
||||||
|
export type mousedown = "mousedown";
|
||||||
|
export type mousemove = "mousemove";
|
||||||
|
export type mouseup = "mouseup";
|
||||||
|
export type mouseleave = "mouseleave";
|
||||||
|
export type dblclick = "dblclick";
|
||||||
|
export type mouseover = "mouseover";
|
||||||
|
export type mouseenter = "mouseenter";
|
||||||
|
export type wheel = "wheel";
|
||||||
|
|
||||||
|
export type keydown = "keydown";
|
||||||
|
export type keyup = "keyup";
|
||||||
|
|
||||||
|
export type blur = "blur";
|
||||||
|
export type focus = "focus";
|
||||||
|
|
||||||
|
export type change = "change";
|
||||||
|
export type input = "input";
|
||||||
|
export type submit = "submit";
|
||||||
|
|
||||||
|
export type resize = "resize";
|
||||||
|
|
||||||
|
export type pause = "pause";
|
||||||
|
export type play = "play";
|
||||||
|
export type ended = "ended";
|
||||||
|
export type timeupdate = "timeupdate";
|
||||||
|
|
||||||
|
export type pointerlockchange = "pointerlockchange";
|
||||||
|
export type pointerlockerror = "pointerlockerror";
|
||||||
|
|
||||||
|
export type load = "load";
|
||||||
|
|
||||||
|
export type selectstart = "selectstart";
|
||||||
|
|
||||||
|
// export type (\w+).+
|
||||||
|
// $1 |
|
||||||
|
export type EventListenerType =
|
||||||
|
click |
|
||||||
|
|
||||||
|
contextmenu |
|
||||||
|
|
||||||
|
touchstart |
|
||||||
|
touchmove |
|
||||||
|
touchcancel |
|
||||||
|
touchend |
|
||||||
|
|
||||||
|
mousedown |
|
||||||
|
mousemove |
|
||||||
|
mouseup |
|
||||||
|
mouseleave |
|
||||||
|
dblclick |
|
||||||
|
mouseover |
|
||||||
|
mouseenter |
|
||||||
|
wheel |
|
||||||
|
|
||||||
|
keydown |
|
||||||
|
keyup |
|
||||||
|
|
||||||
|
blur |
|
||||||
|
focus |
|
||||||
|
change |
|
||||||
|
input |
|
||||||
|
submit |
|
||||||
|
|
||||||
|
resize |
|
||||||
|
|
||||||
|
pause |
|
||||||
|
play |
|
||||||
|
ended |
|
||||||
|
timeupdate |
|
||||||
|
|
||||||
|
pointerlockchange |
|
||||||
|
pointerlockerror |
|
||||||
|
|
||||||
|
load |
|
||||||
|
|
||||||
|
selectstart
|
||||||
|
|
||||||
|
;
|
||||||
|
|
||||||
|
// export type (\w+)\s*=\s*"(.+)"\s*;
|
||||||
|
// static readonly $1:$1 = "$2";
|
||||||
|
export class Events
|
||||||
|
{
|
||||||
|
static readonly MouseButtonLeft = 0;
|
||||||
|
static readonly MouseButtonMiddle = 1;
|
||||||
|
static readonly MouseButtonRight = 2;
|
||||||
|
|
||||||
|
|
||||||
|
static readonly click:click = "click";
|
||||||
|
|
||||||
|
static readonly contextmenu:contextmenu = "contextmenu";
|
||||||
|
|
||||||
|
static readonly touchstart:touchstart = "touchstart";
|
||||||
|
static readonly touchmove:touchmove = "touchmove";
|
||||||
|
static readonly touchcancel:touchcancel = "touchcancel";
|
||||||
|
static readonly touchend:touchend = "touchend";
|
||||||
|
|
||||||
|
static readonly mousedown:mousedown = "mousedown";
|
||||||
|
static readonly mousemove:mousemove = "mousemove";
|
||||||
|
static readonly mouseup:mouseup = "mouseup";
|
||||||
|
static readonly mouseleave:mouseleave = "mouseleave";
|
||||||
|
static readonly dblclick:dblclick = "dblclick";
|
||||||
|
static readonly mouseover:mouseover = "mouseover";
|
||||||
|
static readonly mouseenter:mouseenter = "mouseenter";
|
||||||
|
|
||||||
|
static readonly wheel:wheel = "wheel";
|
||||||
|
|
||||||
|
static readonly keydown:keydown = "keydown";
|
||||||
|
static readonly keyup:keyup = "keyup";
|
||||||
|
|
||||||
|
static readonly blur:blur = "blur";
|
||||||
|
static readonly focus:focus = "focus";
|
||||||
|
static readonly change:change = "change";
|
||||||
|
static readonly input:input = "input";
|
||||||
|
static readonly submit:submit = "submit";
|
||||||
|
|
||||||
|
static readonly resize:resize = "resize";
|
||||||
|
|
||||||
|
static readonly pause:pause = "pause";
|
||||||
|
static readonly play:play = "play";
|
||||||
|
static readonly ended:ended = "ended";
|
||||||
|
static readonly timeupdate:timeupdate = "timeupdate";
|
||||||
|
|
||||||
|
static readonly pointerlockchange:pointerlockchange = "pointerlockchange";
|
||||||
|
static readonly pointerlockerror:pointerlockerror = "pointerlockerror";
|
||||||
|
|
||||||
|
static readonly load = "load";
|
||||||
|
|
||||||
|
static readonly selectstart:selectstart = "selectstart";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static copyMouseEvent( e:MouseEvent )
|
||||||
|
{
|
||||||
|
return new MouseEvent( e.type, e );
|
||||||
|
}
|
||||||
|
|
||||||
|
static copyTouchEvent( e:TouchEvent )
|
||||||
|
{
|
||||||
|
let te = new TouchEvent( e.type, e as any );
|
||||||
|
return te;
|
||||||
|
}
|
||||||
|
|
||||||
|
static copyMouseOrTouchEvent( e:TouchEvent|MouseEvent )
|
||||||
|
{
|
||||||
|
if ( this.isMouseEvent( e ) )
|
||||||
|
{
|
||||||
|
return this.copyMouseEvent( e as MouseEvent );
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.copyTouchEvent( e as TouchEvent );
|
||||||
|
}
|
||||||
|
|
||||||
|
static is( e:Event, ...types:EventListenerType[] )
|
||||||
|
{
|
||||||
|
for ( let t of types )
|
||||||
|
{
|
||||||
|
if ( e.type === t )
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static isType( e:string, ...types:EventListenerType[] )
|
||||||
|
{
|
||||||
|
for ( let t of types )
|
||||||
|
{
|
||||||
|
if ( e === t )
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static isEventOneOf( e:string|Event, ...types:EventListenerType[] )
|
||||||
|
{
|
||||||
|
if ( typeof e === "string" )
|
||||||
|
{
|
||||||
|
for ( let t of types )
|
||||||
|
{
|
||||||
|
if ( e === t )
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( let t of types )
|
||||||
|
{
|
||||||
|
if ( e.type === t )
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static isMouseEvent( e:MouseEvent|TouchEvent )
|
||||||
|
{
|
||||||
|
return /^mouse/.test( e.type ) || ( e.type === Events.dblclick );
|
||||||
|
}
|
||||||
|
|
||||||
|
static îsTouchEvent( e:MouseEvent|TouchEvent )
|
||||||
|
{
|
||||||
|
return /^touch/.test( e.type )
|
||||||
|
}
|
||||||
|
|
||||||
|
static toMouseEvent( e:MouseEvent|TouchEvent )
|
||||||
|
{
|
||||||
|
if ( ! this.isMouseEvent( e ) )
|
||||||
|
{
|
||||||
|
console.log( "Not mouse event:", e.type );
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return e as MouseEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
static toTouchEvent( e:MouseEvent|TouchEvent )
|
||||||
|
{
|
||||||
|
if ( this.îsTouchEvent( e ) )
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return e as TouchEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
static isStart( e:MouseEvent|TouchEvent|string )
|
||||||
|
{
|
||||||
|
return Events.isEventOneOf( e, Events.mousedown, Events.touchstart );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static isMove( e:MouseEvent|TouchEvent|string )
|
||||||
|
{
|
||||||
|
return Events.isEventOneOf( e, Events.mousemove, Events.touchmove );
|
||||||
|
}
|
||||||
|
|
||||||
|
static isEnd( e:MouseEvent|TouchEvent|string )
|
||||||
|
{
|
||||||
|
return Events.isEventOneOf( e, Events.mouseup, Events.touchend, Events.touchcancel );
|
||||||
|
}
|
||||||
|
|
||||||
|
static silent( e:Event )
|
||||||
|
{
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
static once<T>( target:EventTarget, type:EventListenerType, callback:(t:T)=>void )
|
||||||
|
{
|
||||||
|
let onceCallback = ( e:T )=>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
callback( e );
|
||||||
|
}
|
||||||
|
catch( ex )
|
||||||
|
{
|
||||||
|
console.error( ex );
|
||||||
|
}
|
||||||
|
|
||||||
|
target.removeEventListener( type, onceCallback as any );
|
||||||
|
}
|
||||||
|
|
||||||
|
target.addEventListener( type, onceCallback as any );
|
||||||
|
}
|
||||||
|
|
||||||
|
static disableSelection( e:HTMLElement ):Func<void>
|
||||||
|
{
|
||||||
|
let preventer = ( ev:any ) => { ev.preventDefault() };
|
||||||
|
|
||||||
|
OnMouseDown.add( e, preventer );
|
||||||
|
OnSelectStart.add( e, preventer );
|
||||||
|
|
||||||
|
let remover = ()=>
|
||||||
|
{
|
||||||
|
OnMouseDown.remove( e, preventer );
|
||||||
|
OnSelectStart.remove( e, preventer );
|
||||||
|
}
|
||||||
|
|
||||||
|
return remover;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OnPointerLockChange
|
||||||
|
{
|
||||||
|
static add( element:HTMLElement|Document, callback:( e:Event )=>void, options:any = undefined )
|
||||||
|
{
|
||||||
|
document.addEventListener( Events.pointerlockchange, callback, options );
|
||||||
|
}
|
||||||
|
|
||||||
|
static remove( element:HTMLElement|Document, callback:( e:Event )=>void )
|
||||||
|
{
|
||||||
|
document.removeEventListener( Events.pointerlockchange, callback );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OnWheel
|
||||||
|
{
|
||||||
|
static add( element:HTMLElement, callback:( e:WheelEvent )=>void, options:any = undefined )
|
||||||
|
{
|
||||||
|
element.addEventListener( Events.wheel, callback, options );
|
||||||
|
}
|
||||||
|
|
||||||
|
static remove( element:HTMLElement, callback:( e:WheelEvent )=>void )
|
||||||
|
{
|
||||||
|
element.removeEventListener( Events.wheel, callback );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type MouseEventCallback = ( e:MouseEvent )=>void;
|
||||||
|
|
||||||
|
export class WrappedCallback
|
||||||
|
{
|
||||||
|
element:HTMLElement;
|
||||||
|
wrappedCallback:MouseEventCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OnClick
|
||||||
|
{
|
||||||
|
static add( element:HTMLElement, callback:MouseEventCallback, options:any = undefined )
|
||||||
|
{
|
||||||
|
element.addEventListener( Events.click, callback, options );
|
||||||
|
}
|
||||||
|
|
||||||
|
static remove( element:HTMLElement, callback:MouseEventCallback )
|
||||||
|
{
|
||||||
|
element.removeEventListener( Events.click, callback );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OnMouseDown
|
||||||
|
{
|
||||||
|
static add( element:HTMLElement, callback:( e:MouseEvent )=>void, options:any = undefined )
|
||||||
|
{
|
||||||
|
element.addEventListener( Events.mousedown, callback, options );
|
||||||
|
}
|
||||||
|
|
||||||
|
static remove( element:HTMLElement, callback:( e:MouseEvent )=>void )
|
||||||
|
{
|
||||||
|
element.removeEventListener( Events.mousedown, callback );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class OnContextMenu
|
||||||
|
{
|
||||||
|
static add( element:HTMLElement, callback:( e:MouseEvent )=>void, options:any = undefined )
|
||||||
|
{
|
||||||
|
element.addEventListener( Events.contextmenu, callback, options );
|
||||||
|
}
|
||||||
|
|
||||||
|
static remove( element:HTMLElement, callback:( e:MouseEvent )=>void )
|
||||||
|
{
|
||||||
|
element.removeEventListener( Events.contextmenu, callback );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class OnMouseDrag
|
||||||
|
{
|
||||||
|
static noHold = false;
|
||||||
|
|
||||||
|
static add( element:HTMLElement, callback:( e:MouseEvent )=>void|boolean, options:any = undefined )
|
||||||
|
{
|
||||||
|
let noHold = OnMouseDrag.noHold;
|
||||||
|
let isHoldInteraction = null;
|
||||||
|
let downStart = 0;
|
||||||
|
let holdInteractionTreshold = 500;
|
||||||
|
|
||||||
|
let checkAutomaticSwitch = (e:MouseEvent) =>
|
||||||
|
{
|
||||||
|
let time = new Date().getTime();
|
||||||
|
let elapsed = time - downStart;
|
||||||
|
|
||||||
|
if ( elapsed > holdInteractionTreshold )
|
||||||
|
{
|
||||||
|
removeEvents( e );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Cursor.lock();
|
||||||
|
//UIComponent.lockCursor( UICursors.releaseDragging + "" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let removeEvents = ( e:MouseEvent )=>
|
||||||
|
{
|
||||||
|
if ( noHold )
|
||||||
|
{
|
||||||
|
let mouseUpEvent = new MouseEvent( Events.mouseup, e );
|
||||||
|
callback( mouseUpEvent );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
callback( e );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
OnMouseMove.remove( document.body, callback );
|
||||||
|
|
||||||
|
if ( noHold )
|
||||||
|
{
|
||||||
|
OnMouseUp.remove( document.body, checkAutomaticSwitch );
|
||||||
|
OnMouseDown.remove( document.body, removeEvents );
|
||||||
|
//UIComponent.freeCursor();
|
||||||
|
|
||||||
|
Cursor.free();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
OnMouseUp.remove( document.body, removeEvents );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
let addEvents = ()=>
|
||||||
|
{
|
||||||
|
OnMouseMove.add( document.body, callback );
|
||||||
|
|
||||||
|
if ( noHold )
|
||||||
|
{
|
||||||
|
OnMouseUp.add( document.body, checkAutomaticSwitch );
|
||||||
|
OnMouseDown.add( document.body, removeEvents );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
OnMouseUp.add( document.body, removeEvents );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
let onDown = ( e:MouseEvent )=>
|
||||||
|
{
|
||||||
|
let result = callback( e );
|
||||||
|
|
||||||
|
if ( result === false )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
downStart = new Date().getTime();
|
||||||
|
isHoldInteraction = true;
|
||||||
|
|
||||||
|
addEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
OnMouseDown.add( element, onDown );
|
||||||
|
|
||||||
|
|
||||||
|
return onDown;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static remove( element:HTMLElement, callback:( e:MouseEvent )=>void )
|
||||||
|
{
|
||||||
|
OnMouseDown.remove( element, callback );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OnMouseOver
|
||||||
|
{
|
||||||
|
static add( element:HTMLElement, callback:( e:MouseEvent )=>void, options:any = undefined )
|
||||||
|
{
|
||||||
|
element.addEventListener( Events.mouseover, callback, options );
|
||||||
|
}
|
||||||
|
|
||||||
|
static remove( element:HTMLElement, callback:( e:MouseEvent )=>void )
|
||||||
|
{
|
||||||
|
element.removeEventListener( Events.mouseover, callback );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OnMouseEnter
|
||||||
|
{
|
||||||
|
static add( element:HTMLElement, callback:( e:MouseEvent )=>void, options:any = undefined )
|
||||||
|
{
|
||||||
|
element.addEventListener( Events.mouseenter, callback, options );
|
||||||
|
}
|
||||||
|
|
||||||
|
static remove( element:HTMLElement, callback:( e:MouseEvent )=>void )
|
||||||
|
{
|
||||||
|
element.removeEventListener( Events.mouseenter, callback );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OnDoubleClick
|
||||||
|
{
|
||||||
|
static add( element:HTMLElement, callback:( e:MouseEvent )=>void, options:any = undefined )
|
||||||
|
{
|
||||||
|
element.addEventListener( Events.dblclick, callback, options );
|
||||||
|
}
|
||||||
|
|
||||||
|
static remove( element:HTMLElement, callback:( e:MouseEvent )=>void )
|
||||||
|
{
|
||||||
|
element.removeEventListener( Events.dblclick, callback );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OnMouseMove
|
||||||
|
{
|
||||||
|
static add( element:HTMLElement, callback:( e:MouseEvent )=>void, options:any = undefined )
|
||||||
|
{
|
||||||
|
element.addEventListener( Events.mousemove, callback, options );
|
||||||
|
}
|
||||||
|
|
||||||
|
static remove( element:HTMLElement, callback:( e:MouseEvent )=>void )
|
||||||
|
{
|
||||||
|
element.removeEventListener( Events.mousemove, callback );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OnMouseUp
|
||||||
|
{
|
||||||
|
static add( element:HTMLElement, callback:( e:MouseEvent )=>void, options:any = undefined )
|
||||||
|
{
|
||||||
|
element.addEventListener( Events.mouseup, callback, options );
|
||||||
|
}
|
||||||
|
|
||||||
|
static remove( element:HTMLElement, callback:( e:MouseEvent )=>void )
|
||||||
|
{
|
||||||
|
element.removeEventListener( Events.mouseup, callback );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OnMouseLeave
|
||||||
|
{
|
||||||
|
static add( element:HTMLElement, callback:( e:MouseEvent )=>void, options:any = undefined )
|
||||||
|
{
|
||||||
|
element.addEventListener( Events.mouseleave, callback, options );
|
||||||
|
}
|
||||||
|
|
||||||
|
static remove( element:HTMLElement, callback:( e:MouseEvent )=>void )
|
||||||
|
{
|
||||||
|
element.removeEventListener( Events.mouseleave, callback );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OnKeyDown
|
||||||
|
{
|
||||||
|
static add( element:HTMLElement, callback:( e:KeyboardEvent )=>void, options:any = undefined )
|
||||||
|
{
|
||||||
|
element.addEventListener( Events.keydown, callback, options );
|
||||||
|
}
|
||||||
|
|
||||||
|
static remove( element:HTMLElement, callback:( e:KeyboardEvent )=>void )
|
||||||
|
{
|
||||||
|
element.removeEventListener( Events.keydown, callback );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OnKeyUp
|
||||||
|
{
|
||||||
|
static add( element:HTMLElement, callback:( e:KeyboardEvent )=>void, options:any = undefined )
|
||||||
|
{
|
||||||
|
element.addEventListener( Events.keyup, callback, options );
|
||||||
|
}
|
||||||
|
|
||||||
|
static remove( element:HTMLElement, callback:( e:KeyboardEvent )=>void )
|
||||||
|
{
|
||||||
|
element.removeEventListener( Events.keyup, callback );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OnTouchStart
|
||||||
|
{
|
||||||
|
static add( element:HTMLElement, callback:( e:TouchEvent )=>void, options:any = undefined )
|
||||||
|
{
|
||||||
|
element.addEventListener( Events.touchstart, callback, options );
|
||||||
|
}
|
||||||
|
|
||||||
|
static remove( element:HTMLElement, callback:( e:TouchEvent )=>void )
|
||||||
|
{
|
||||||
|
element.removeEventListener( Events.touchstart, callback );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export class OnTouchEnd
|
||||||
|
{
|
||||||
|
static add( element:HTMLElement, callback:( e:TouchEvent )=>void, options:any = undefined )
|
||||||
|
{
|
||||||
|
element.addEventListener( Events.touchend, callback, options );
|
||||||
|
}
|
||||||
|
|
||||||
|
static remove( element:HTMLElement, callback:( e:TouchEvent )=>void )
|
||||||
|
{
|
||||||
|
element.removeEventListener( Events.touchend, callback );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OnTouchCancel
|
||||||
|
{
|
||||||
|
static add( element:HTMLElement, callback:( e:TouchEvent )=>void, options:any = undefined )
|
||||||
|
{
|
||||||
|
element.addEventListener( Events.touchcancel, callback, options );
|
||||||
|
}
|
||||||
|
|
||||||
|
static remove( element:HTMLElement, callback:( e:TouchEvent )=>void )
|
||||||
|
{
|
||||||
|
element.removeEventListener( Events.touchcancel, callback );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OnTouchMove
|
||||||
|
{
|
||||||
|
static add( element:HTMLElement, callback:( e:TouchEvent )=>void, options:any = undefined )
|
||||||
|
{
|
||||||
|
element.addEventListener( Events.touchmove, callback, options );
|
||||||
|
}
|
||||||
|
|
||||||
|
static remove( element:HTMLElement, callback:( e:TouchEvent )=>void )
|
||||||
|
{
|
||||||
|
element.removeEventListener( Events.touchmove, callback );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OnTouchDrag
|
||||||
|
{
|
||||||
|
static add( element:HTMLElement, callback:( e:TouchEvent )=>void, options:any = undefined )
|
||||||
|
{
|
||||||
|
OnTouchStart.add( element, callback, options );
|
||||||
|
OnTouchMove.add( element, callback, options );
|
||||||
|
OnTouchEnd.add( element, callback, options );
|
||||||
|
}
|
||||||
|
|
||||||
|
static remove( element:HTMLElement, callback:( e:TouchEvent )=>void )
|
||||||
|
{
|
||||||
|
OnTouchStart.remove( element, callback );
|
||||||
|
OnTouchMove.remove( element, callback );
|
||||||
|
OnTouchEnd.remove( element, callback );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OnDrag
|
||||||
|
{
|
||||||
|
static add( element:HTMLElement, callback:( e:TouchEvent|MouseEvent )=>void, options:any = undefined )
|
||||||
|
{
|
||||||
|
OnTouchDrag.add( element, callback );
|
||||||
|
let mouseCallback = OnMouseDrag.add( element, callback );
|
||||||
|
|
||||||
|
return { touch:callback, mouse:mouseCallback };
|
||||||
|
}
|
||||||
|
|
||||||
|
static remove( element:HTMLElement, callbackData:{ touch:()=>void, mouse:()=>void } )
|
||||||
|
{
|
||||||
|
OnTouchDrag.add( element, callbackData.touch );
|
||||||
|
OnMouseDrag.add( element, callbackData.mouse );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export type HORIZONTAL = "HORIZONTAL";
|
||||||
|
export type VERTICAL = "VERTICAL";
|
||||||
|
export type BOTH = "BOTH";
|
||||||
|
|
||||||
|
export type SwipeType = HORIZONTAL | VERTICAL | BOTH;
|
||||||
|
|
||||||
|
export class OnSwipe
|
||||||
|
{
|
||||||
|
static readonly HORIZONTAL:HORIZONTAL = "HORIZONTAL";
|
||||||
|
static readonly VERTICAL:VERTICAL = "VERTICAL";
|
||||||
|
static readonly BOTH:BOTH = "BOTH";
|
||||||
|
|
||||||
|
static add( type:SwipeType, treshold:number, element:HTMLElement, callback:( e:TouchEvent )=>void, options:any = undefined )
|
||||||
|
{
|
||||||
|
let onStart =
|
||||||
|
( startEvent:TouchEvent )=>
|
||||||
|
{
|
||||||
|
let checkingMovement = true;
|
||||||
|
let active = false;
|
||||||
|
let startPosition = DOMHitTest.getPointerPosition( startEvent );
|
||||||
|
|
||||||
|
let onDrag = ( dragEvent:TouchEvent )=>
|
||||||
|
{
|
||||||
|
if ( checkingMovement )
|
||||||
|
{
|
||||||
|
let position = DOMHitTest.getPointerPosition( dragEvent );
|
||||||
|
let difference = position.sub( startPosition );
|
||||||
|
let moved = difference.length > treshold;
|
||||||
|
|
||||||
|
if ( moved )
|
||||||
|
{
|
||||||
|
if ( OnSwipe.HORIZONTAL === type )
|
||||||
|
{
|
||||||
|
active = Math.abs( difference.x ) > Math.abs( difference.y );
|
||||||
|
}
|
||||||
|
else if ( OnSwipe.VERTICAL === type )
|
||||||
|
{
|
||||||
|
active = Math.abs( difference.y ) > Math.abs( difference.x );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
active = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
checkingMovement = false;
|
||||||
|
callback( startEvent );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! active )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
callback( dragEvent );
|
||||||
|
|
||||||
|
dragEvent.preventDefault();
|
||||||
|
dragEvent.stopImmediatePropagation();
|
||||||
|
dragEvent.stopPropagation();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
let removeListeners = ( endEvent :TouchEvent ) =>
|
||||||
|
{
|
||||||
|
if ( active )
|
||||||
|
{
|
||||||
|
callback( endEvent );
|
||||||
|
|
||||||
|
endEvent.preventDefault();
|
||||||
|
endEvent.stopImmediatePropagation();
|
||||||
|
endEvent.stopPropagation();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
OnTouchMove.remove( element, onDrag );
|
||||||
|
OnTouchCancel.remove( element, removeListeners );
|
||||||
|
OnTouchEnd.remove( element, removeListeners );
|
||||||
|
}
|
||||||
|
|
||||||
|
OnTouchMove.add( element, onDrag );
|
||||||
|
OnTouchCancel.add( element, removeListeners );
|
||||||
|
OnTouchEnd.add( element, removeListeners );
|
||||||
|
};
|
||||||
|
|
||||||
|
OnTouchStart.add( element, onStart );
|
||||||
|
|
||||||
|
return onStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
static remove( element:HTMLElement, callback:( e:TouchEvent )=>void )
|
||||||
|
{
|
||||||
|
OnTouchStart.remove( element, callback );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OnBlur
|
||||||
|
{
|
||||||
|
static add( element:HTMLElement, callback:( e:FocusEvent )=>void, options:any = undefined )
|
||||||
|
{
|
||||||
|
element.addEventListener( Events.blur, callback, options );
|
||||||
|
}
|
||||||
|
|
||||||
|
static remove( element:HTMLElement, callback:( e:FocusEvent )=>void )
|
||||||
|
{
|
||||||
|
element.removeEventListener( Events.blur, callback );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OnFocus
|
||||||
|
{
|
||||||
|
static add( element:HTMLElement, callback:( e:FocusEvent )=>void, options:any = undefined )
|
||||||
|
{
|
||||||
|
element.addEventListener( Events.focus, callback, options );
|
||||||
|
}
|
||||||
|
|
||||||
|
static remove( element:HTMLElement, callback:( e:FocusEvent )=>void )
|
||||||
|
{
|
||||||
|
element.removeEventListener( Events.focus, callback );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OnChange
|
||||||
|
{
|
||||||
|
static add( element:HTMLElement|HTMLInputElement, callback:( e:Event )=>void, options:any = undefined )
|
||||||
|
{
|
||||||
|
element.addEventListener( Events.change, callback, options );
|
||||||
|
}
|
||||||
|
|
||||||
|
static remove( element:HTMLElement|HTMLInputElement, callback:( e:Event )=>void )
|
||||||
|
{
|
||||||
|
element.removeEventListener( Events.change, callback );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OnInput
|
||||||
|
{
|
||||||
|
static add( element:HTMLElement, callback:( e:InputEvent )=>void, options:any = undefined )
|
||||||
|
{
|
||||||
|
( element as any ).addEventListener( Events.input, callback, options );
|
||||||
|
}
|
||||||
|
|
||||||
|
static remove( element:HTMLElement, callback:( e:InputEvent )=>void )
|
||||||
|
{
|
||||||
|
( element as any ).removeEventListener( Events.input, callback );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OnSubmit
|
||||||
|
{
|
||||||
|
static add( element:HTMLFormElement, callback:( e:Event )=>void, options:any = undefined )
|
||||||
|
{
|
||||||
|
element.addEventListener( Events.submit, callback, options );
|
||||||
|
}
|
||||||
|
|
||||||
|
static remove( element:HTMLFormElement, callback:( e:Event )=>void )
|
||||||
|
{
|
||||||
|
element.removeEventListener( Events.submit, callback );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OnResize
|
||||||
|
{
|
||||||
|
static add( window:Window, callback:( e:Event )=>void, options:any = undefined )
|
||||||
|
{
|
||||||
|
window.addEventListener( Events.resize, callback, options );
|
||||||
|
}
|
||||||
|
|
||||||
|
static remove( window:Window, callback:( e:Event )=>void )
|
||||||
|
{
|
||||||
|
window.removeEventListener( Events.resize, callback );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OnPlay
|
||||||
|
{
|
||||||
|
static add( audioElement:HTMLAudioElement, callback:( e:Event )=>void, options:any = undefined )
|
||||||
|
{
|
||||||
|
audioElement.addEventListener( Events.play, callback, options );
|
||||||
|
}
|
||||||
|
|
||||||
|
static remove( audioElement:HTMLAudioElement, callback:( e:Event )=>void )
|
||||||
|
{
|
||||||
|
audioElement.removeEventListener( Events.play, callback );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OnPause
|
||||||
|
{
|
||||||
|
static add( audioElement:HTMLAudioElement, callback:( e:Event )=>void, options:any = undefined )
|
||||||
|
{
|
||||||
|
audioElement.addEventListener( Events.pause, callback, options );
|
||||||
|
}
|
||||||
|
|
||||||
|
static remove( audioElement:HTMLAudioElement, callback:( e:Event )=>void )
|
||||||
|
{
|
||||||
|
audioElement.removeEventListener( Events.pause, callback );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OnEnded
|
||||||
|
{
|
||||||
|
static add( audioElement:HTMLAudioElement, callback:( e:Event )=>void, options:any = undefined )
|
||||||
|
{
|
||||||
|
audioElement.addEventListener( Events.ended, callback, options );
|
||||||
|
}
|
||||||
|
|
||||||
|
static remove( audioElement:HTMLAudioElement, callback:( e:Event )=>void )
|
||||||
|
{
|
||||||
|
audioElement.removeEventListener( Events.ended, callback );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OnTimeUpdate
|
||||||
|
{
|
||||||
|
static add( audioElement:HTMLAudioElement, callback:( e:Event )=>void, options:any = undefined )
|
||||||
|
{
|
||||||
|
audioElement.addEventListener( Events.timeupdate, callback, options );
|
||||||
|
}
|
||||||
|
|
||||||
|
static remove( audioElement:HTMLAudioElement, callback:( e:Event )=>void )
|
||||||
|
{
|
||||||
|
audioElement.removeEventListener( Events.timeupdate, callback );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OnLoad
|
||||||
|
{
|
||||||
|
static add( element:HTMLScriptElement|FileReader, callback:( e:Event )=>void, options:any = undefined )
|
||||||
|
{
|
||||||
|
element.addEventListener( Events.load, callback, options );
|
||||||
|
}
|
||||||
|
|
||||||
|
static remove( element:HTMLScriptElement|FileReader, callback:( e:Event )=>void )
|
||||||
|
{
|
||||||
|
element.removeEventListener( Events.load, callback );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OnSelectStart
|
||||||
|
{
|
||||||
|
static add( element:HTMLElement, callback:( e:Event )=>void, options:any = undefined )
|
||||||
|
{
|
||||||
|
element.addEventListener( Events.selectstart, callback, options );
|
||||||
|
}
|
||||||
|
|
||||||
|
static remove( element:HTMLElement, callback:( e:Event )=>void )
|
||||||
|
{
|
||||||
|
element.removeEventListener( Events.selectstart, callback );
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,372 @@
|
||||||
|
|
||||||
|
|
||||||
|
import { ClassFlag } from "./ClassFlag";
|
||||||
|
import { HTMLNodeTreeWalker } from '../graphs/HTMLNodeTreeWalker';
|
||||||
|
import { Box2 } from "../geometry/Box2";
|
||||||
|
import { Vector2 } from "../geometry/Vector2";
|
||||||
|
import { ElementAttribute } from "./ElementAttribute";
|
||||||
|
import { MapList } from "../tools/MapList";
|
||||||
|
|
||||||
|
export type above_sight = "above-sight";
|
||||||
|
export type in_sight = "in-sight";
|
||||||
|
export type below_sight = "below-sight";
|
||||||
|
|
||||||
|
export type InsighStateType = above_sight | in_sight | below_sight;
|
||||||
|
|
||||||
|
export class InsighState
|
||||||
|
{
|
||||||
|
static readonly above_sight:above_sight = "above-sight";
|
||||||
|
static readonly in_sight:in_sight = "in-sight";
|
||||||
|
static readonly below_sight:below_sight = "below-sight";
|
||||||
|
}
|
||||||
|
|
||||||
|
export class InsightData
|
||||||
|
{
|
||||||
|
insight:Insight;
|
||||||
|
isInsight = false;
|
||||||
|
wasInsight = false;
|
||||||
|
viewBox:Box2 = null;
|
||||||
|
elementBox:Box2 = new Box2();
|
||||||
|
changeTime:number = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class InsightElement extends Element
|
||||||
|
{
|
||||||
|
|
||||||
|
_insightData:InsightData;
|
||||||
|
|
||||||
|
static ensure( element:Element )
|
||||||
|
{
|
||||||
|
let insightElement = element as InsightElement;
|
||||||
|
|
||||||
|
if ( ! insightElement._insightData )
|
||||||
|
{
|
||||||
|
insightElement._insightData = new InsightData();
|
||||||
|
}
|
||||||
|
|
||||||
|
return insightElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
static forAllInsightElements( root:Element, callback:( insightElement:InsightElement )=>void )
|
||||||
|
{
|
||||||
|
let walker = HTMLNodeTreeWalker.$;
|
||||||
|
|
||||||
|
walker.forAll( root,
|
||||||
|
n =>
|
||||||
|
{
|
||||||
|
let insightElement = n as InsightElement;
|
||||||
|
|
||||||
|
if ( insightElement._insightData )
|
||||||
|
{
|
||||||
|
callback( insightElement );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type InsightCallback = ()=>void;
|
||||||
|
export type CallbackMapList = Map<Element,InsightCallback[]>;
|
||||||
|
|
||||||
|
|
||||||
|
export class Insight
|
||||||
|
{
|
||||||
|
static attribute = new ElementAttribute( "insight" );
|
||||||
|
static wasSeenFlag = new ClassFlag( "was-in-sight" );
|
||||||
|
static maxHeight = new ElementAttribute( "max-insight-height" );
|
||||||
|
|
||||||
|
static readonly above_sight = new ClassFlag( InsighState.above_sight );
|
||||||
|
static readonly in_sight = new ClassFlag( InsighState.in_sight );
|
||||||
|
static readonly below_sight = new ClassFlag( InsighState.below_sight );
|
||||||
|
|
||||||
|
static changeFrequencyMS = 200;
|
||||||
|
private _elements:Element[] = [];
|
||||||
|
private _offsightCallbackElements:CallbackMapList;
|
||||||
|
private _insightCallbackElements:CallbackMapList;
|
||||||
|
|
||||||
|
private _viewBoxSize:Vector2 = null;
|
||||||
|
private _hasScreenUpdate = false;
|
||||||
|
private _lastYOffset = 0;
|
||||||
|
private _yTresholdForChanges = 15;
|
||||||
|
private _lastGrabbedTime = 0;
|
||||||
|
private _afterGrabingUpdateDurationMS = 3000;
|
||||||
|
|
||||||
|
static _allInstances:Insight[] = [];
|
||||||
|
|
||||||
|
static updateAll()
|
||||||
|
{
|
||||||
|
this._allInstances.forEach( i => { i._hasScreenUpdate = true; i.update() } );
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor( publishGlobally:boolean = true )
|
||||||
|
{
|
||||||
|
if ( publishGlobally )
|
||||||
|
{
|
||||||
|
Insight._allInstances.push( this );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onInsight( element:Element, callback:InsightCallback )
|
||||||
|
{
|
||||||
|
MapList.add( this._insightCallbackElements, element, callback );
|
||||||
|
}
|
||||||
|
|
||||||
|
onOffsight( element:Element, callback:InsightCallback )
|
||||||
|
{
|
||||||
|
MapList.add( this._offsightCallbackElements, element, callback );
|
||||||
|
}
|
||||||
|
|
||||||
|
grabElements()
|
||||||
|
{
|
||||||
|
this._lastGrabbedTime = new Date().getTime();
|
||||||
|
|
||||||
|
this._elements.forEach(
|
||||||
|
e =>
|
||||||
|
{
|
||||||
|
let element = InsightElement.ensure( e );
|
||||||
|
element._insightData.insight = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
this._elements = Insight.attribute.queryAll( document.body );
|
||||||
|
this._offsightCallbackElements = new Map<Element,InsightCallback[]>();
|
||||||
|
this._insightCallbackElements = new Map<Element,InsightCallback[]>();
|
||||||
|
|
||||||
|
this._elements.forEach(
|
||||||
|
e =>
|
||||||
|
{
|
||||||
|
let element = InsightElement.ensure( e );
|
||||||
|
element._insightData.insight = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
//console.log( this._elements );
|
||||||
|
}
|
||||||
|
|
||||||
|
update()
|
||||||
|
{
|
||||||
|
this._updateViewBox();
|
||||||
|
|
||||||
|
let currentTime = new Date().getTime();
|
||||||
|
let elapsedSinceGrabbing = currentTime - this._lastGrabbedTime;
|
||||||
|
|
||||||
|
if ( elapsedSinceGrabbing < this._afterGrabingUpdateDurationMS )
|
||||||
|
{
|
||||||
|
this._hasScreenUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let yChange = window.pageYOffset - this._lastYOffset;
|
||||||
|
|
||||||
|
if ( Math.abs( yChange ) > this._yTresholdForChanges )
|
||||||
|
{
|
||||||
|
this._hasScreenUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! this._hasScreenUpdate )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._lastYOffset = window.pageYOffset;
|
||||||
|
this._hasScreenUpdate = false;
|
||||||
|
|
||||||
|
let now = new Date().getTime();
|
||||||
|
this._elements.forEach( e => this.updateElement( e, now ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
updateElement( e:Element, updateTime:number )
|
||||||
|
{
|
||||||
|
let insightElement = InsightElement.ensure( e );
|
||||||
|
let insightData = insightElement._insightData;
|
||||||
|
|
||||||
|
let viewBox = insightData.viewBox;
|
||||||
|
|
||||||
|
if ( ! viewBox )
|
||||||
|
{
|
||||||
|
viewBox = this._getViewBox( e );
|
||||||
|
insightData.viewBox = viewBox;
|
||||||
|
}
|
||||||
|
|
||||||
|
let elementBox = insightData.elementBox;
|
||||||
|
|
||||||
|
|
||||||
|
Box2.updatefromClientRect( insightData.elementBox, e.getBoundingClientRect() );
|
||||||
|
|
||||||
|
if ( Insight.maxHeight.in( e ) )
|
||||||
|
{
|
||||||
|
let maxHeight = Insight.maxHeight.from( e );
|
||||||
|
let isPercentage = /\%/.test( maxHeight );
|
||||||
|
let maxHeightPixel = parseFloat( maxHeight.replace( /\%|px/, "" ) );
|
||||||
|
|
||||||
|
if ( isPercentage )
|
||||||
|
{
|
||||||
|
maxHeightPixel *= window.innerHeight / 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
let size = elementBox.size;
|
||||||
|
|
||||||
|
if ( size.y > maxHeightPixel )
|
||||||
|
{
|
||||||
|
let center = elementBox.center;
|
||||||
|
let offset = maxHeightPixel / 2;
|
||||||
|
elementBox.min.y = center.y - offset;
|
||||||
|
elementBox.max.y = center.y + offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
let insight = viewBox.intersectsBox( elementBox );
|
||||||
|
/*
|
||||||
|
console.log(
|
||||||
|
"Is in sight", insight,
|
||||||
|
"DOCUMENT VIEW BOX:", viewBox,
|
||||||
|
"ELEMENT BOX:", elementBox,
|
||||||
|
"ELEMENT", e
|
||||||
|
);*/
|
||||||
|
|
||||||
|
if ( insight === insightData.isInsight )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let timeElapsedSinceLastChange = updateTime - insightData.changeTime;
|
||||||
|
|
||||||
|
|
||||||
|
if ( timeElapsedSinceLastChange <= Insight.changeFrequencyMS )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
insightData.changeTime = updateTime;
|
||||||
|
insightData.isInsight = insight;
|
||||||
|
|
||||||
|
if ( insight )
|
||||||
|
{
|
||||||
|
Insight.in_sight.setAs( e, true );
|
||||||
|
Insight.above_sight.setAs( e, false);
|
||||||
|
Insight.below_sight.setAs( e, false );
|
||||||
|
|
||||||
|
if ( this._insightCallbackElements.has( e ) )
|
||||||
|
{
|
||||||
|
this._insightCallbackElements.get( e ).forEach( c => c() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
let isAbove = elementBox.max.y < viewBox.min.y;
|
||||||
|
Insight.in_sight.setAs( e, false );
|
||||||
|
Insight.above_sight.setAs( e, isAbove);
|
||||||
|
Insight.below_sight.setAs( e, ! isAbove );
|
||||||
|
|
||||||
|
if ( this._offsightCallbackElements.has( e ) )
|
||||||
|
{
|
||||||
|
this._offsightCallbackElements.get( e ).forEach( c => c() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if ( insightData.wasInsight )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout( ()=>{ Insight.wasSeenFlag.set( e ); }, 200 );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private _updateViewBox()
|
||||||
|
{
|
||||||
|
if ( ! this._elements )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! this._needsViewBoxUpdate() )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._hasScreenUpdate = true;
|
||||||
|
|
||||||
|
this._viewBoxSize.x = window.innerWidth;
|
||||||
|
this._viewBoxSize.y = window.innerHeight;
|
||||||
|
|
||||||
|
this._elements.forEach(
|
||||||
|
e =>
|
||||||
|
{
|
||||||
|
let insightElement = InsightElement.ensure( e );
|
||||||
|
let data = insightElement._insightData;
|
||||||
|
data.viewBox = this._getViewBox( e );;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private _needsViewBoxUpdate()
|
||||||
|
{
|
||||||
|
if ( this._viewBoxSize == null )
|
||||||
|
{
|
||||||
|
this._viewBoxSize = new Vector2( 0, 0 );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( this._viewBoxSize.x !== window.innerWidth || this._viewBoxSize.y !== window.innerHeight )
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private _getViewBox( e:Element ):Box2
|
||||||
|
{
|
||||||
|
|
||||||
|
let relativeStart = 15;
|
||||||
|
let relativeEnd = 100 - relativeStart;
|
||||||
|
|
||||||
|
let coords = Insight.attribute.from( e );
|
||||||
|
|
||||||
|
if ( coords && coords.length > 0 )
|
||||||
|
{
|
||||||
|
let numbers = coords.split( "," );
|
||||||
|
|
||||||
|
if ( numbers.length == 1 )
|
||||||
|
{
|
||||||
|
relativeStart = parseFloat( numbers[ 0 ] );
|
||||||
|
relativeEnd = 100 - relativeStart;
|
||||||
|
}
|
||||||
|
else if ( numbers.length == 2 )
|
||||||
|
{
|
||||||
|
relativeStart = parseFloat( numbers[ 0 ] );
|
||||||
|
relativeEnd = 100 - parseFloat( numbers[ 1 ] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( isNaN( relativeStart ) )
|
||||||
|
{
|
||||||
|
relativeStart = 15;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( isNaN( relativeEnd ) )
|
||||||
|
{
|
||||||
|
relativeEnd = 100 - relativeStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//console.log( "VIEW BOX START/END", relativeStart, relativeEnd );
|
||||||
|
|
||||||
|
let startY = window.innerHeight * relativeStart / 100;
|
||||||
|
let endY = window.innerHeight * relativeEnd / 100;
|
||||||
|
|
||||||
|
return new Box2( new Vector2( -10000, startY ), new Vector2( 10000, endY ) );
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
import { DOMOrientation, DOMOrientationType } from "../dom/DOMOrientation";
|
||||||
|
import { EventSlot } from "../events/EventSlot";
|
||||||
|
import { ElementType } from "./ElementType";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export class LandscapeScale
|
||||||
|
{
|
||||||
|
private _styleElement:Element;
|
||||||
|
private _lastWidth:number;
|
||||||
|
private _lastHeight:number;
|
||||||
|
private _lastOrientation:DOMOrientationType;
|
||||||
|
private _ultraWideRatio:number = 2.2;
|
||||||
|
private _sw:number;
|
||||||
|
private _sh:number;
|
||||||
|
private _swidth:number;
|
||||||
|
private _sheight:number;
|
||||||
|
private _sx:number;
|
||||||
|
private _sy:number;
|
||||||
|
private _spx:number;
|
||||||
|
|
||||||
|
get sw()
|
||||||
|
{
|
||||||
|
return this._sw;
|
||||||
|
}
|
||||||
|
|
||||||
|
onScreenSizeChange = new EventSlot<LandscapeScale>();
|
||||||
|
onOrientationChange = new EventSlot<LandscapeScale>();
|
||||||
|
|
||||||
|
|
||||||
|
update()
|
||||||
|
{
|
||||||
|
let newWidth = window.innerWidth;
|
||||||
|
let newHeight = window.innerHeight;
|
||||||
|
let newOrientation = DOMOrientation.type;
|
||||||
|
|
||||||
|
if ( newWidth === this._lastWidth && newHeight === this._lastHeight )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
this.onScreenSizeChange.dispatch( this );
|
||||||
|
|
||||||
|
if ( this._lastOrientation != newOrientation )
|
||||||
|
{
|
||||||
|
this._lastOrientation = newOrientation;
|
||||||
|
this.onOrientationChange.dispatch( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( this._styleElement == null )
|
||||||
|
{
|
||||||
|
this._styleElement = ElementType.style.create();
|
||||||
|
document.body.appendChild( this._styleElement );
|
||||||
|
}
|
||||||
|
|
||||||
|
let ratio = newWidth / newHeight;
|
||||||
|
this._lastWidth = newWidth;
|
||||||
|
this._lastHeight = newHeight;
|
||||||
|
|
||||||
|
this._sw = newWidth / 100;
|
||||||
|
|
||||||
|
if ( ratio > this._ultraWideRatio )
|
||||||
|
{
|
||||||
|
this._sw *= ( this._ultraWideRatio / ratio );
|
||||||
|
this._sh = this._sw / 16 * 9;
|
||||||
|
|
||||||
|
this._sx = ( newWidth - ( this._sw * 100 ) ) / 2;
|
||||||
|
this._sy = 0;
|
||||||
|
|
||||||
|
this._spx = ratio / this._ultraWideRatio;
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this._spx = 1;
|
||||||
|
this._sx = 0;
|
||||||
|
this._sy = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
this._swidth = this._sw * 100;
|
||||||
|
this._sheight = this._sh * 100;
|
||||||
|
|
||||||
|
|
||||||
|
this._styleElement.innerHTML =
|
||||||
|
`
|
||||||
|
:root
|
||||||
|
{
|
||||||
|
--sw:${this._sw}px;
|
||||||
|
--sh:${this._sh}px;
|
||||||
|
--sx:${this._sx}px;
|
||||||
|
--sy:${this._sy}px;
|
||||||
|
--swidth:${this._swidth}px;
|
||||||
|
--sheight:${this._sheight}px;
|
||||||
|
--spx:${this._spx}px;
|
||||||
|
--spx-raw:${this._spx};
|
||||||
|
--spx-inv:${1/this._spx}px;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
export enum LinkType
|
||||||
|
{
|
||||||
|
RELATIVE,
|
||||||
|
ROOT,
|
||||||
|
ABSOLUTE
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Link
|
||||||
|
{
|
||||||
|
type:LinkType;
|
||||||
|
path:string;
|
||||||
|
|
||||||
|
|
||||||
|
clone()
|
||||||
|
{
|
||||||
|
let c = new Link();
|
||||||
|
|
||||||
|
c.type = this.type;
|
||||||
|
c.path = this.path;
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
prettify()
|
||||||
|
{
|
||||||
|
let clone = this.clone();
|
||||||
|
|
||||||
|
if ( clone.path.endsWith( "index.php" ) )
|
||||||
|
{
|
||||||
|
clone.path = clone.path.replace( /index\.php$/, "" );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( clone.path.endsWith( ".php" ) )
|
||||||
|
{
|
||||||
|
clone.path = clone.path.replace( /\.php$/, "" );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( clone.path.endsWith( "/" ) )
|
||||||
|
{
|
||||||
|
clone.path = clone.path.replace( /\/$/, "" );
|
||||||
|
}
|
||||||
|
|
||||||
|
return clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Root( path:string )
|
||||||
|
{
|
||||||
|
let c = new Link();
|
||||||
|
|
||||||
|
c.type = LinkType.ROOT;
|
||||||
|
c.path = path;
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Absolute( path:string )
|
||||||
|
{
|
||||||
|
let c = new Link();
|
||||||
|
|
||||||
|
c.type = LinkType.ABSOLUTE;
|
||||||
|
c.path = path;
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Relative( path:string )
|
||||||
|
{
|
||||||
|
let c = new Link();
|
||||||
|
|
||||||
|
c.type = LinkType.RELATIVE;
|
||||||
|
c.path = path;
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,167 @@
|
||||||
|
|
||||||
|
import { Loader } from "../xhttp/Loader";
|
||||||
|
import { PageHandler } from "./PageHandler";
|
||||||
|
|
||||||
|
export enum PageDataState
|
||||||
|
{
|
||||||
|
INITIAL,
|
||||||
|
LOADING,
|
||||||
|
SUCCESSED,
|
||||||
|
FAILED
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PageData
|
||||||
|
{
|
||||||
|
private _pageHandler:PageHandler;
|
||||||
|
private _rawHTML:string;
|
||||||
|
get rawHTML(){ return this._rawHTML;}
|
||||||
|
private _pageRootStartIndex:number;
|
||||||
|
private _pageRootEndIndex:number;
|
||||||
|
private _title:string;
|
||||||
|
private _state:PageDataState = PageDataState.INITIAL;
|
||||||
|
private _absolutePath:string;
|
||||||
|
private _relativePath:string;
|
||||||
|
private _cacheTime:number;
|
||||||
|
private _language:string = null;
|
||||||
|
|
||||||
|
scrollTop:number;
|
||||||
|
|
||||||
|
constructor( pageHandler:PageHandler, absolutePath:string, relativePath:string )
|
||||||
|
{
|
||||||
|
this._pageHandler = pageHandler;
|
||||||
|
this._absolutePath = absolutePath;
|
||||||
|
this._relativePath = relativePath;
|
||||||
|
this._state = PageDataState.INITIAL;
|
||||||
|
this.scrollTop = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
async load():Promise<void>
|
||||||
|
{
|
||||||
|
if ( this.isLoading )
|
||||||
|
{
|
||||||
|
return await this._waitUntilLoaded();
|
||||||
|
}
|
||||||
|
|
||||||
|
this._state = PageDataState.LOADING;
|
||||||
|
|
||||||
|
let path = this._absolutePath;
|
||||||
|
|
||||||
|
let rawHTML:string = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
let pageParameters = this._pageHandler.pageParameters;
|
||||||
|
this._cacheTime = new Date().getTime();
|
||||||
|
let pageURL = path + pageParameters + "&time=" + this._cacheTime;
|
||||||
|
console.log( pageURL );
|
||||||
|
rawHTML = await Loader.loadText( pageURL );
|
||||||
|
this._rawHTML = rawHTML;
|
||||||
|
this._state = PageDataState.SUCCESSED;
|
||||||
|
}
|
||||||
|
catch( e )
|
||||||
|
{
|
||||||
|
this._rawHTML = null;
|
||||||
|
this._state = PageDataState.FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _waitUntilLoaded():Promise<void>
|
||||||
|
{
|
||||||
|
let pageData = this;
|
||||||
|
|
||||||
|
let promise = new Promise<void>
|
||||||
|
(
|
||||||
|
( resolve, reject ) =>
|
||||||
|
{
|
||||||
|
let checkForReadiness = ()=>
|
||||||
|
{
|
||||||
|
if ( pageData.ready )
|
||||||
|
{
|
||||||
|
resolve();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout( checkForReadiness, 100 );
|
||||||
|
}
|
||||||
|
|
||||||
|
checkForReadiness();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return Promise.resolve( promise );
|
||||||
|
}
|
||||||
|
|
||||||
|
get ready()
|
||||||
|
{
|
||||||
|
return this._state === PageDataState.SUCCESSED || this._state === PageDataState.FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isLoading()
|
||||||
|
{
|
||||||
|
return this._state === PageDataState.LOADING;
|
||||||
|
}
|
||||||
|
|
||||||
|
get title()
|
||||||
|
{
|
||||||
|
if ( ! this._title )
|
||||||
|
{
|
||||||
|
let titleStartTag = "<title>";
|
||||||
|
let titleEndTag = "</title>";
|
||||||
|
|
||||||
|
let titleStart = this._rawHTML.indexOf( titleStartTag ) + titleStartTag.length;
|
||||||
|
let titleEnd = this._rawHTML.indexOf( titleEndTag, titleStart )
|
||||||
|
|
||||||
|
this._title = this._rawHTML.substring( titleStart, titleEnd );
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._title;
|
||||||
|
}
|
||||||
|
|
||||||
|
get language()
|
||||||
|
{
|
||||||
|
if ( ! this._language )
|
||||||
|
{
|
||||||
|
let langStartMatcher = "<html lang=\"";
|
||||||
|
let langEndMatcher = "\"";
|
||||||
|
|
||||||
|
let langStart = this._rawHTML.indexOf( langStartMatcher );
|
||||||
|
|
||||||
|
if ( langStart === -1 )
|
||||||
|
{
|
||||||
|
this._language = "en";
|
||||||
|
return this._language;
|
||||||
|
}
|
||||||
|
|
||||||
|
let langEnd = this._rawHTML.indexOf( langEndMatcher, langStart + langStartMatcher.length + 1 );
|
||||||
|
|
||||||
|
let snippet = this._rawHTML.substring( langStart, langEnd );
|
||||||
|
|
||||||
|
let result = /\"(.+)/.exec( snippet );
|
||||||
|
|
||||||
|
this._language = result[ 1 ];
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._language;
|
||||||
|
}
|
||||||
|
|
||||||
|
get pageRootInnerHTML()
|
||||||
|
{
|
||||||
|
if ( ! this._pageRootStartIndex )
|
||||||
|
{
|
||||||
|
let pageRootTag = this._pageHandler.pageRootTag;
|
||||||
|
|
||||||
|
let pageRootStartTagBegin = this._rawHTML.indexOf( `<${pageRootTag}` );
|
||||||
|
let pageRootStartEnd = this._rawHTML.indexOf( ">", pageRootStartTagBegin ) + 1;
|
||||||
|
let pageRootEnd = this._rawHTML.lastIndexOf( `</${pageRootTag}>` );
|
||||||
|
|
||||||
|
this._pageRootStartIndex = pageRootStartEnd;
|
||||||
|
this._pageRootEndIndex = pageRootEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return this._rawHTML.substring( this._pageRootStartIndex, this._pageRootEndIndex );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,7 @@
|
||||||
|
import { PageHandler } from "./PageHandler";
|
||||||
|
|
||||||
|
export abstract class PageTransitionHandler
|
||||||
|
{
|
||||||
|
abstract onTransitionOut( pageHandler:PageHandler ):Promise<void>;
|
||||||
|
abstract onTransitionIn( pageHandler:PageHandler ):Promise<void>;
|
||||||
|
}
|
|
@ -0,0 +1,228 @@
|
||||||
|
|
||||||
|
import { HTMLNodeTreeWalker } from "../graphs/HTMLNodeTreeWalker";
|
||||||
|
import { DOMNameSpaces } from "./DOMNameSpaces";
|
||||||
|
import { ElementAttribute } from "./ElementAttribute";
|
||||||
|
|
||||||
|
export class ResolvablePathAttribute
|
||||||
|
{
|
||||||
|
private _selector:string;
|
||||||
|
private _attribute:ElementAttribute
|
||||||
|
|
||||||
|
constructor( selector:string, attribute:string, namespace?:string )
|
||||||
|
{
|
||||||
|
this._selector = selector;
|
||||||
|
this._attribute = new ElementAttribute( attribute, false, namespace );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
get combinedSelector()
|
||||||
|
{
|
||||||
|
let combined = `${this._selector}${this._attribute.selector}`;
|
||||||
|
|
||||||
|
return combined;
|
||||||
|
}
|
||||||
|
|
||||||
|
get useNodeNameMatcher()
|
||||||
|
{
|
||||||
|
return this._selector === "image";
|
||||||
|
}
|
||||||
|
|
||||||
|
getFrom( target:Element|Document )
|
||||||
|
{
|
||||||
|
if ( this.useNodeNameMatcher )
|
||||||
|
{
|
||||||
|
let matchedElements:Element[] = [];
|
||||||
|
|
||||||
|
let walker = new HTMLNodeTreeWalker();
|
||||||
|
|
||||||
|
let walkable = target;
|
||||||
|
if ( ( target as Document ).documentElement )
|
||||||
|
{
|
||||||
|
walkable = ( target as Document).documentElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
walker.forAll( walkable,
|
||||||
|
n =>
|
||||||
|
{
|
||||||
|
if ( n.nodeName === this._selector )
|
||||||
|
{
|
||||||
|
matchedElements.push( n as Element );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return matchedElements;
|
||||||
|
}
|
||||||
|
|
||||||
|
let elements = target.querySelectorAll( this.combinedSelector );
|
||||||
|
|
||||||
|
return Array.prototype.slice.call( elements );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
process( element:Element, rootToken:string, rootPath:string )
|
||||||
|
{
|
||||||
|
let value = this._attribute.from( element );
|
||||||
|
|
||||||
|
if ( value === null )
|
||||||
|
{
|
||||||
|
if ( this._selector === "image" )
|
||||||
|
{
|
||||||
|
console.log( "no value for image" );
|
||||||
|
this.lgGetAttribute( element, "href" );
|
||||||
|
this.lgGetAttribute( element, "xlink:href" );
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if ( this._selector === "image" )
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*if ( ! value.startsWith( rootToken ) )
|
||||||
|
{
|
||||||
|
if ( this._selector === "image" )
|
||||||
|
{
|
||||||
|
let shortValue = value;
|
||||||
|
|
||||||
|
if ( value.length > 100 )
|
||||||
|
{
|
||||||
|
shortValue = value.substring( 0, 100 );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
else if ( this._attribute.name === "style" )
|
||||||
|
{
|
||||||
|
value = value.replace( rootToken, rootPath )
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
value = value.replace( rootToken, rootPath )
|
||||||
|
|
||||||
|
this._attribute.to( element, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
lgGetAttribute( element:Element, attribute:string )
|
||||||
|
{
|
||||||
|
this.lg( `${attribute} >> ${element.getAttribute( attribute )} `);
|
||||||
|
this.lg( `${attribute} NS=null >> ${element.getAttributeNS( null, attribute )} `);
|
||||||
|
this.lg( `${attribute} NS="" >> ${element.getAttributeNS( "", attribute )} `);
|
||||||
|
this.lg( `${attribute} NS=XLink >> ${element.getAttributeNS( DOMNameSpaces.XLink, attribute )} `);
|
||||||
|
this.lg( `${attribute} NS=SVG >> ${element.getAttributeNS( DOMNameSpaces.SVG, attribute )} `);
|
||||||
|
}
|
||||||
|
|
||||||
|
lg( value:string )
|
||||||
|
{
|
||||||
|
let shortValue = value;
|
||||||
|
|
||||||
|
if ( value.length > 100 )
|
||||||
|
{
|
||||||
|
shortValue = value.substring( 0, 100 ) + "...";
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log( shortValue );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class RootPathResolver
|
||||||
|
{
|
||||||
|
static readonly customResolve = new ElementAttribute( "resolve-root-path" );
|
||||||
|
|
||||||
|
static readonly resolvables:ResolvablePathAttribute[] =
|
||||||
|
[
|
||||||
|
new ResolvablePathAttribute( "link", "href" ),
|
||||||
|
new ResolvablePathAttribute( "script", "src" ),
|
||||||
|
new ResolvablePathAttribute( "a", "href" ),
|
||||||
|
new ResolvablePathAttribute( "img", "src" ),
|
||||||
|
new ResolvablePathAttribute( "audio", "src" ),
|
||||||
|
new ResolvablePathAttribute( "source", "src" ),
|
||||||
|
new ResolvablePathAttribute( "video", "poster" ),
|
||||||
|
new ResolvablePathAttribute( "video", "src" ),
|
||||||
|
new ResolvablePathAttribute( "*", "style" ),
|
||||||
|
new ResolvablePathAttribute( "image", "xlink:href" ),
|
||||||
|
new ResolvablePathAttribute( "*", "data-load-audio-element" ),
|
||||||
|
new ResolvablePathAttribute( "meta", "content" ),
|
||||||
|
new ResolvablePathAttribute( "*", "data-root-href" ),
|
||||||
|
]
|
||||||
|
|
||||||
|
private static _rootToken = "::/";
|
||||||
|
|
||||||
|
static clearRootPath( rootPath:string )
|
||||||
|
{
|
||||||
|
return rootPath.replace( /^\:\:\//, "" );
|
||||||
|
}
|
||||||
|
|
||||||
|
static isRootPath( path:string )
|
||||||
|
{
|
||||||
|
return path.startsWith( RootPathResolver._rootToken );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static get rootToken()
|
||||||
|
{
|
||||||
|
return RootPathResolver._rootToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
getRootPath( relativeFilePath:string )
|
||||||
|
{
|
||||||
|
let normalizedPath = relativeFilePath.replace( /\\/g, "/" );
|
||||||
|
|
||||||
|
if ( normalizedPath.startsWith( "/" ) )
|
||||||
|
{
|
||||||
|
normalizedPath = normalizedPath.substring( 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
let numParentDirectories = normalizedPath.split( "/" ).length - 1;
|
||||||
|
let rootPath = "";
|
||||||
|
|
||||||
|
for ( let i = 0; i < numParentDirectories; i++ )
|
||||||
|
{
|
||||||
|
rootPath += "../";
|
||||||
|
}
|
||||||
|
|
||||||
|
return rootPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
process( target:Element|Document, relativeFilePath:string )
|
||||||
|
{
|
||||||
|
let rootPath = this.getRootPath( relativeFilePath );
|
||||||
|
|
||||||
|
RootPathResolver.resolvables.forEach(
|
||||||
|
( resolvable )=>
|
||||||
|
{
|
||||||
|
let elements = resolvable.getFrom( target );
|
||||||
|
|
||||||
|
elements.forEach(
|
||||||
|
( e:Element ) =>
|
||||||
|
resolvable.process( e, RootPathResolver.rootToken, rootPath )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
RootPathResolver.customResolve.forAll(
|
||||||
|
target,
|
||||||
|
( e:Element ) =>
|
||||||
|
{
|
||||||
|
let attName = RootPathResolver.customResolve.from( e );
|
||||||
|
let att = new ElementAttribute( attName, false );
|
||||||
|
let path = att.from( e );
|
||||||
|
|
||||||
|
console.log( `Processing custom root path "${attName}" = "${path}"` );
|
||||||
|
if ( path === null )
|
||||||
|
{
|
||||||
|
console.log( "Root Path Resolve: No path found in ", attName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
path = path.replace( RootPathResolver.rootToken, rootPath );
|
||||||
|
att.to( e, path );
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,121 @@
|
||||||
|
|
||||||
|
import { ElementAttribute } from "./ElementAttribute";
|
||||||
|
import { UserAgentInfo } from "./UserAgentInfo";
|
||||||
|
|
||||||
|
export type Android = "android";
|
||||||
|
export type Tablet = "tablet";
|
||||||
|
export type iPhone = "iphone";
|
||||||
|
export type iPad = "ipad";
|
||||||
|
export type Mac = "mac";
|
||||||
|
export type PC = "pc";
|
||||||
|
export type TV = "tv";
|
||||||
|
export type Other = "Other";
|
||||||
|
|
||||||
|
// export type (\w+)\s*=\s*".+;
|
||||||
|
// $1 |
|
||||||
|
export type UserAgentDeviceType =
|
||||||
|
|
||||||
|
Android |
|
||||||
|
Tablet |
|
||||||
|
iPhone |
|
||||||
|
iPad |
|
||||||
|
Mac |
|
||||||
|
PC |
|
||||||
|
TV |
|
||||||
|
Other
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
export class UserAgentDeviceTypes
|
||||||
|
{
|
||||||
|
// export type (\w+)\s*=\s*"(.+)".*;
|
||||||
|
// static readonly $1:$1 = "$2";
|
||||||
|
|
||||||
|
static readonly iPhone:iPhone = "iphone";
|
||||||
|
static readonly iPad:iPad = "ipad";
|
||||||
|
static readonly Android:Android = "android";
|
||||||
|
static readonly Tablet:Tablet = "tablet";
|
||||||
|
static readonly Mac:Mac = "mac";
|
||||||
|
static readonly PC:PC = "pc";
|
||||||
|
static readonly TV:TV = "tv";
|
||||||
|
static readonly Other:Other = "Other";
|
||||||
|
|
||||||
|
private static _deviceType:UserAgentDeviceType = null;
|
||||||
|
|
||||||
|
static get current()
|
||||||
|
{
|
||||||
|
if ( UserAgentDeviceTypes._deviceType !== null )
|
||||||
|
{
|
||||||
|
return UserAgentDeviceTypes._deviceType;
|
||||||
|
}
|
||||||
|
|
||||||
|
UserAgentDeviceTypes._deviceType = UserAgentDeviceTypes._guess();
|
||||||
|
|
||||||
|
return UserAgentDeviceTypes._deviceType;
|
||||||
|
}
|
||||||
|
|
||||||
|
static readonly attribute = new ElementAttribute( "device-type" );
|
||||||
|
|
||||||
|
static setOnBody()
|
||||||
|
{
|
||||||
|
UserAgentDeviceTypes.attribute.to( document.body, UserAgentDeviceTypes.current );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static _guess():UserAgentDeviceType
|
||||||
|
{
|
||||||
|
if ( UserAgentInfo.isIPad )
|
||||||
|
{
|
||||||
|
return UserAgentDeviceTypes.iPad;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( UserAgentInfo.isIPhone )
|
||||||
|
{
|
||||||
|
return UserAgentDeviceTypes.iPhone;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( UserAgentInfo.isMac )
|
||||||
|
{
|
||||||
|
if ( UserAgentInfo.isTV )
|
||||||
|
{
|
||||||
|
return UserAgentDeviceTypes.TV;
|
||||||
|
}
|
||||||
|
|
||||||
|
return UserAgentDeviceTypes.Mac;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( UserAgentInfo.isAndroid )
|
||||||
|
{
|
||||||
|
if ( UserAgentInfo.isTV )
|
||||||
|
{
|
||||||
|
return UserAgentDeviceTypes.TV;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( UserAgentInfo.isTablet )
|
||||||
|
{
|
||||||
|
return UserAgentDeviceTypes.Tablet;
|
||||||
|
}
|
||||||
|
|
||||||
|
return UserAgentDeviceTypes.Android;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( UserAgentInfo.isTablet )
|
||||||
|
{
|
||||||
|
return UserAgentDeviceTypes.Tablet;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( UserAgentInfo.isTV )
|
||||||
|
{
|
||||||
|
return UserAgentDeviceTypes.TV;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( UserAgentInfo.isOther )
|
||||||
|
{
|
||||||
|
return UserAgentDeviceTypes.Other;
|
||||||
|
}
|
||||||
|
|
||||||
|
return UserAgentDeviceTypes.PC;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
export class UserAgentInfo
|
||||||
|
{
|
||||||
|
static is( regex:RegExp )
|
||||||
|
{
|
||||||
|
return regex.test( navigator.userAgent );
|
||||||
|
}
|
||||||
|
|
||||||
|
static get isIPhone()
|
||||||
|
{
|
||||||
|
return UserAgentInfo.is( /iphone/i );
|
||||||
|
}
|
||||||
|
|
||||||
|
static get isIPad()
|
||||||
|
{
|
||||||
|
return UserAgentInfo.is( /ipad/i );
|
||||||
|
}
|
||||||
|
|
||||||
|
static get isMac()
|
||||||
|
{
|
||||||
|
return UserAgentInfo.is( /macintosh/i );
|
||||||
|
}
|
||||||
|
|
||||||
|
static get isAndroid()
|
||||||
|
{
|
||||||
|
return UserAgentInfo.is( /android/i );
|
||||||
|
}
|
||||||
|
|
||||||
|
static get isTablet()
|
||||||
|
{
|
||||||
|
return UserAgentInfo.is( /tablet/i );
|
||||||
|
}
|
||||||
|
|
||||||
|
static get isTV()
|
||||||
|
{
|
||||||
|
let isTV = UserAgentInfo.is( /smart\-?tv/i ) || UserAgentInfo.is( /net\-?cast/i ) ||
|
||||||
|
UserAgentInfo.is( /apple\s*tv/i ) || UserAgentInfo.is( /android\s*tv/i ) ||
|
||||||
|
UserAgentInfo.is( /opera\s*tv/i );
|
||||||
|
|
||||||
|
return isTV;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get isOther()
|
||||||
|
{
|
||||||
|
let other = UserAgentInfo.is( /roku/i ) || UserAgentInfo.is( /tizen/i ) ||
|
||||||
|
UserAgentInfo.is( /netflix/i ) || UserAgentInfo.is( /crkey/i );
|
||||||
|
|
||||||
|
return other;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
import { Range } from "./Range";
|
||||||
|
import { Vector2 } from "./Vector2";
|
||||||
|
|
||||||
|
export class Box2
|
||||||
|
{
|
||||||
|
min:Vector2;
|
||||||
|
max:Vector2;
|
||||||
|
|
||||||
|
constructor( min:Vector2 = null, max:Vector2 = null )
|
||||||
|
{
|
||||||
|
this.min = min;
|
||||||
|
this.max = max;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
get size():Vector2
|
||||||
|
{
|
||||||
|
return this.max.clone().sub( this.min );
|
||||||
|
}
|
||||||
|
|
||||||
|
get center():Vector2
|
||||||
|
{
|
||||||
|
return this.max.clone().add( this.min ).multiply( 0.5 );
|
||||||
|
}
|
||||||
|
|
||||||
|
get rangeX():Range
|
||||||
|
{
|
||||||
|
return new Range( this.min.x, this.max.x );
|
||||||
|
}
|
||||||
|
|
||||||
|
get rangeY():Range
|
||||||
|
{
|
||||||
|
return new Range( this.min.y, this.max.y );
|
||||||
|
}
|
||||||
|
|
||||||
|
intersectsBox( box:Box2 )
|
||||||
|
{
|
||||||
|
return this.rangeX.overlaps( box.rangeX ) && this.rangeY.overlaps( box.rangeY );
|
||||||
|
}
|
||||||
|
|
||||||
|
translate( offset:Vector2 )
|
||||||
|
{
|
||||||
|
this.min.add( offset );
|
||||||
|
this.max.add( offset );
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromClientRect( clientRect:ClientRect|DOMRect )
|
||||||
|
{
|
||||||
|
let min = new Vector2( clientRect.left, clientRect.top );
|
||||||
|
let max = new Vector2( clientRect.right, clientRect.bottom );
|
||||||
|
|
||||||
|
return new Box2( min, max );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static toDomRect( box:Box2 )
|
||||||
|
{
|
||||||
|
let size = box.size;
|
||||||
|
let domRect = new DOMRect( box.min.x, box.min.y, size.x, size.y );
|
||||||
|
return domRect;
|
||||||
|
}
|
||||||
|
|
||||||
|
static updatefromClientRect( box:Box2, clientRect:ClientRect|DOMRect )
|
||||||
|
{
|
||||||
|
box.min.set( clientRect.left, clientRect.top );
|
||||||
|
box.max.set( clientRect.right, clientRect.bottom );
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
export class Vector2
|
||||||
|
{
|
||||||
|
x:number = 0;
|
||||||
|
y:number = 0;
|
||||||
|
|
||||||
|
constructor( x:number = 0, y:number = 0 )
|
||||||
|
{
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
clone():Vector2
|
||||||
|
{
|
||||||
|
return new Vector2( this.x, this.y );
|
||||||
|
}
|
||||||
|
|
||||||
|
set( x:number, y:number )
|
||||||
|
{
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
add( other:Vector2 )
|
||||||
|
{
|
||||||
|
this.x += other.x;
|
||||||
|
this.y += other.y;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub( other:Vector2 )
|
||||||
|
{
|
||||||
|
this.x -= other.x;
|
||||||
|
this.y -= other.y;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
multiply( value:number )
|
||||||
|
{
|
||||||
|
this.x *= value;
|
||||||
|
this.y *= value;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
get length()
|
||||||
|
{
|
||||||
|
return Math.sqrt( this.x * this.x + this.y * this.y );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -834,7 +834,6 @@ export class RegExpUtility
|
||||||
|
|
||||||
static createRegExp( regexp:RegExp, matching:string, replacement:string )
|
static createRegExp( regexp:RegExp, matching:string, replacement:string )
|
||||||
{
|
{
|
||||||
|
|
||||||
let source = regexp.source;
|
let source = regexp.source;
|
||||||
let flags = regexp.flags;
|
let flags = regexp.flags;
|
||||||
source = source.replace( matching, replacement );
|
source = source.replace( matching, replacement );
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
export class MapList
|
||||||
|
{
|
||||||
|
static add<K,V>( map:Map<K,V[]>, k:K, v:V )
|
||||||
|
{
|
||||||
|
if ( ! map.has( k ) )
|
||||||
|
{
|
||||||
|
map.set( k, [] );
|
||||||
|
}
|
||||||
|
|
||||||
|
map.get( k ).push( v );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -83,7 +83,7 @@ export class Loader
|
||||||
xhr.open( method, url, true );
|
xhr.open( method, url, true );
|
||||||
xhr.responseType = "json";
|
xhr.responseType = "json";
|
||||||
|
|
||||||
console.log( "xhr", url, method );
|
// console.log( "xhr", url, method );
|
||||||
|
|
||||||
xhr.onload=()=>
|
xhr.onload=()=>
|
||||||
{
|
{
|
||||||
|
@ -129,7 +129,7 @@ export class Loader
|
||||||
|
|
||||||
xhr.onload = () =>
|
xhr.onload = () =>
|
||||||
{
|
{
|
||||||
console.log( "load", url, xhr );
|
// console.log( "load", url, xhr );
|
||||||
|
|
||||||
if ( xhr.status !== 200 )
|
if ( xhr.status !== 200 )
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
import { RegExpUtility } from "../../browser/text/RegExpUtitlity";
|
import { RegExpUtility } from "../../browser/text/RegExpUtitlity";
|
||||||
import { LogColors } from "./LogColors";
|
import { LogColors } from "./LogColors";
|
||||||
|
|
||||||
let pr = ( window as any ).process;
|
|
||||||
|
|
||||||
export class RJLog
|
export class RJLog
|
||||||
{
|
{
|
||||||
|
@ -100,7 +99,7 @@ export class RJLog
|
||||||
|
|
||||||
static error( ...params:any[] )
|
static error( ...params:any[] )
|
||||||
{
|
{
|
||||||
if ( RJLog.logAlwaysLineInfo || typeof pr === "object" )
|
if ( RJLog.logAlwaysLineInfo || typeof process === "object" )
|
||||||
{
|
{
|
||||||
let lineInfo = RJLog.getLineInfo( RJLog.errorColor );
|
let lineInfo = RJLog.getLineInfo( RJLog.errorColor );
|
||||||
console.log( "\n" + lineInfo );
|
console.log( "\n" + lineInfo );
|
||||||
|
@ -112,7 +111,7 @@ export class RJLog
|
||||||
|
|
||||||
static warn( ...params:any[] )
|
static warn( ...params:any[] )
|
||||||
{
|
{
|
||||||
if ( RJLog.logAlwaysLineInfo || typeof pr === "object" )
|
if ( RJLog.logAlwaysLineInfo || typeof process === "object" )
|
||||||
{
|
{
|
||||||
let lineInfo = RJLog.getLineInfo( RJLog.warnColor );
|
let lineInfo = RJLog.getLineInfo( RJLog.warnColor );
|
||||||
console.log( "\n" + lineInfo );
|
console.log( "\n" + lineInfo );
|
||||||
|
@ -124,7 +123,7 @@ export class RJLog
|
||||||
|
|
||||||
static log( ...params:any[] )
|
static log( ...params:any[] )
|
||||||
{
|
{
|
||||||
if ( RJLog.logAlwaysLineInfo || typeof pr === "object" )
|
if ( RJLog.logAlwaysLineInfo || typeof process === "object" )
|
||||||
{
|
{
|
||||||
let lineInfo = RJLog.getLineInfo();
|
let lineInfo = RJLog.getLineInfo();
|
||||||
console.log( "\n" + lineInfo );
|
console.log( "\n" + lineInfo );
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
{
|
{
|
||||||
"compilerOptions":
|
"compilerOptions": {
|
||||||
{
|
"target": "ESNext",
|
||||||
"lib": ["ES2020"],
|
"module": "CommonJS",
|
||||||
"types": ["node"], // Includes Node.js types
|
"lib": ["ESNext"],
|
||||||
},
|
"moduleResolution": "Node",
|
||||||
|
"types": ["node"]
|
||||||
|
}
|
||||||
"include": ["./*"]
|
|
||||||
}
|
}
|
|
@ -0,0 +1,111 @@
|
||||||
|
|
||||||
|
import * as fs from "fs";
|
||||||
|
import * as path from "path";
|
||||||
|
import { RJLog } from "../log/RJLog";
|
||||||
|
|
||||||
|
export class PagesInfo
|
||||||
|
{
|
||||||
|
pages:string[] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PHPPagesBuilder
|
||||||
|
{
|
||||||
|
inputDir:string;
|
||||||
|
outputDir:string;
|
||||||
|
|
||||||
|
constructor( source:string, build:string )
|
||||||
|
{
|
||||||
|
this.inputDir = source;
|
||||||
|
this.outputDir = build;
|
||||||
|
}
|
||||||
|
|
||||||
|
filterFiles( fileName:string )
|
||||||
|
{
|
||||||
|
if ( fileName.startsWith( "__" ) )
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileName.endsWith( ".html" );
|
||||||
|
}
|
||||||
|
|
||||||
|
modify( content:string )
|
||||||
|
{
|
||||||
|
return `<?php ?>\n${content}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
apply( compiler:any )
|
||||||
|
{
|
||||||
|
compiler.hooks.afterCompile.tapAsync( "PHPPagesBuilder",
|
||||||
|
( compilation:any, callback:any ) =>
|
||||||
|
{
|
||||||
|
|
||||||
|
if ( fs.existsSync( this.inputDir ) )
|
||||||
|
{
|
||||||
|
fs.readdirSync( this.inputDir )
|
||||||
|
.filter( this.filterFiles )
|
||||||
|
.forEach(
|
||||||
|
( file ) =>
|
||||||
|
{
|
||||||
|
let fullPath = path.join(this.inputDir, file);
|
||||||
|
compilation.fileDependencies.add(fullPath); // Mark for watching
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
callback();
|
||||||
|
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
compiler.hooks.emit.tapAsync( "PHPPagesBuilder",
|
||||||
|
( compilation:any, callback:any ) =>
|
||||||
|
{
|
||||||
|
fs.readdir( this.inputDir,
|
||||||
|
( err, files ) =>
|
||||||
|
{
|
||||||
|
if ( err )
|
||||||
|
{
|
||||||
|
RJLog.log( "Error", this.inputDir );
|
||||||
|
return callback( err );
|
||||||
|
}
|
||||||
|
|
||||||
|
let pages = new PagesInfo();
|
||||||
|
files.filter( this.filterFiles ).forEach( ( file ) =>
|
||||||
|
{
|
||||||
|
let filePath = path.join( this.inputDir, file );
|
||||||
|
let content = fs.readFileSync( filePath, "utf-8" );
|
||||||
|
let modifiedContent = this.modify( content );
|
||||||
|
let phpFileName = file.replace( ".html", ".php" );
|
||||||
|
let outputFileName = path.join( this.outputDir, phpFileName );
|
||||||
|
|
||||||
|
pages.pages.push( phpFileName );
|
||||||
|
|
||||||
|
compilation.assets[ outputFileName ] =
|
||||||
|
{
|
||||||
|
source: () => modifiedContent,
|
||||||
|
size: () => modifiedContent.length,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
let pagesPath = path.join( this.outputDir, "pages.json" );
|
||||||
|
|
||||||
|
let pagesJSON = JSON.stringify( pages, null, " " );
|
||||||
|
|
||||||
|
compilation.assets[ pagesPath ] =
|
||||||
|
{
|
||||||
|
source: () => pagesJSON,
|
||||||
|
size: () => pagesJSON.length,
|
||||||
|
};
|
||||||
|
|
||||||
|
callback();
|
||||||
|
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue