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 );
|
||||
}
|
||||
|
||||
}
|
|
@ -833,8 +833,7 @@ export class RegExpUtility
|
|||
|
||||
|
||||
static createRegExp( regexp:RegExp, matching:string, replacement:string )
|
||||
{
|
||||
|
||||
{
|
||||
let source = regexp.source;
|
||||
let flags = regexp.flags;
|
||||
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.responseType = "json";
|
||||
|
||||
console.log( "xhr", url, method );
|
||||
// console.log( "xhr", url, method );
|
||||
|
||||
xhr.onload=()=>
|
||||
{
|
||||
|
@ -129,7 +129,7 @@ export class Loader
|
|||
|
||||
xhr.onload = () =>
|
||||
{
|
||||
console.log( "load", url, xhr );
|
||||
// console.log( "load", url, xhr );
|
||||
|
||||
if ( xhr.status !== 200 )
|
||||
{
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
import { RegExpUtility } from "../../browser/text/RegExpUtitlity";
|
||||
import { LogColors } from "./LogColors";
|
||||
|
||||
let pr = ( window as any ).process;
|
||||
|
||||
export class RJLog
|
||||
{
|
||||
|
@ -100,7 +99,7 @@ export class RJLog
|
|||
|
||||
static error( ...params:any[] )
|
||||
{
|
||||
if ( RJLog.logAlwaysLineInfo || typeof pr === "object" )
|
||||
if ( RJLog.logAlwaysLineInfo || typeof process === "object" )
|
||||
{
|
||||
let lineInfo = RJLog.getLineInfo( RJLog.errorColor );
|
||||
console.log( "\n" + lineInfo );
|
||||
|
@ -112,7 +111,7 @@ export class RJLog
|
|||
|
||||
static warn( ...params:any[] )
|
||||
{
|
||||
if ( RJLog.logAlwaysLineInfo || typeof pr === "object" )
|
||||
if ( RJLog.logAlwaysLineInfo || typeof process === "object" )
|
||||
{
|
||||
let lineInfo = RJLog.getLineInfo( RJLog.warnColor );
|
||||
console.log( "\n" + lineInfo );
|
||||
|
@ -124,7 +123,7 @@ export class RJLog
|
|||
|
||||
static log( ...params:any[] )
|
||||
{
|
||||
if ( RJLog.logAlwaysLineInfo || typeof pr === "object" )
|
||||
if ( RJLog.logAlwaysLineInfo || typeof process === "object" )
|
||||
{
|
||||
let lineInfo = RJLog.getLineInfo();
|
||||
console.log( "\n" + lineInfo );
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
{
|
||||
"compilerOptions":
|
||||
{
|
||||
"lib": ["ES2020"],
|
||||
"types": ["node"], // Includes Node.js types
|
||||
},
|
||||
|
||||
|
||||
"include": ["./*"]
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"module": "CommonJS",
|
||||
"lib": ["ESNext"],
|
||||
"moduleResolution": "Node",
|
||||
"types": ["node"]
|
||||
}
|
||||
}
|
|
@ -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