1270 lines
29 KiB
TypeScript
1270 lines
29 KiB
TypeScript
|
|
import { PageData } from "./PageData";
|
|
import { PageTransitionHandler } from "./PageTransitionHandler";
|
|
import { sleep } from "../animation/sleep";
|
|
import { EventSlot } from "../events/EventSlot";
|
|
import { ElementAttribute } from "./ElementAttribute";
|
|
import { OnClick, OnMouseDown, OnMouseEnter } from "./EventListeners";
|
|
import { LanguageCode } from "../i18n/LanguageCode";
|
|
import { Link, LinkType } from "./LinkResolver";
|
|
import { RootPathResolver } from "./RootPathResolver";
|
|
import { Loader } from "../xhttp/Loader";
|
|
import { ElementType } from "./ElementType";
|
|
import { RegExpUtility } from "../text/RegExpUtitlity";
|
|
import { AttributeValue } from "./AttributeValue";
|
|
|
|
export class FunctionHandleAnchor extends HTMLAnchorElement
|
|
{
|
|
_functionCallback:()=>void;
|
|
_assignedClickListener = false;
|
|
}
|
|
|
|
export class PageRequestEvent
|
|
{
|
|
lastPage:string;
|
|
nextPage:string;
|
|
}
|
|
|
|
export class PagesJSON
|
|
{
|
|
pages:string[];
|
|
}
|
|
|
|
export enum PageHandlerMode
|
|
{
|
|
PHP,
|
|
HTML,
|
|
ELECTRON
|
|
}
|
|
|
|
export class PageHandler
|
|
{
|
|
private _pagesPath = "_scripts/pages.json";
|
|
private _pages = new Map<string,PageData>();
|
|
private _startPage:string;
|
|
get startPage(){ return this._startPage;}
|
|
private _currentPage:string;
|
|
private _nextPage:string;
|
|
private _isLoading:boolean = false;
|
|
private _followUpPage:string = null;
|
|
private _pageRootTag = "body";
|
|
private _forceHTTPS = true;
|
|
private _pageParameters = "?no-page-token=true";
|
|
readonly onPageRequest = new EventSlot<PageRequestEvent>();
|
|
get pageRootTag(){ return this._pageRootTag; }
|
|
transitionHandler:PageTransitionHandler;
|
|
|
|
private _webAdress:string;
|
|
private _localAdressExtension:string;
|
|
|
|
private _rootPathResolver = new RootPathResolver();
|
|
get rootPathResolver(){ return this._rootPathResolver; }
|
|
|
|
setPagesPath( path:string )
|
|
{
|
|
this._pagesPath = path;
|
|
}
|
|
|
|
setAdress( localExtension: string, web:string )
|
|
{
|
|
this._localAdressExtension = localExtension;
|
|
this._webAdress = web;
|
|
}
|
|
|
|
setPageRootTag( pageRootTag:string )
|
|
{
|
|
this._pageRootTag = pageRootTag;
|
|
}
|
|
|
|
get pageParameters()
|
|
{
|
|
return this._pageParameters;
|
|
}
|
|
|
|
getParameterValue( name:string )
|
|
{
|
|
let parameters = window.location.search;
|
|
let pageParameters = parameters.length > 0 ? parameters.substring( 1 ) : null;
|
|
|
|
if ( ! pageParameters )
|
|
{
|
|
return null;
|
|
}
|
|
|
|
let pairs = pageParameters.split( "&" );
|
|
|
|
for ( let p of pairs )
|
|
{
|
|
let keyValue = p.split( "=" );
|
|
|
|
if ( keyValue.length !== 2 )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ( keyValue[ 0 ] === name )
|
|
{
|
|
return decodeURIComponent( keyValue[ 1 ] );
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
private _preloadImagesFlag:boolean = true;
|
|
private _maxPreloadingDurationMS:number = 2000;
|
|
|
|
private static _localHost = "http://localhost:8080";
|
|
private static _localNetworkRegexPattern = /^(http:\/\/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}?:XXXX)/
|
|
private static _localNetworkRegex = RegExpUtility.createRegExp( this._localNetworkRegexPattern, "XXXX", 8080 + "" );
|
|
private _isHTMLMode:boolean = false;
|
|
private _fileLocation:string = "";
|
|
private _isFileMode:boolean = false;
|
|
|
|
static setLocalHostPort( port:number )
|
|
{
|
|
PageHandler._localHost = "http://localhost:" + port;
|
|
PageHandler._localNetworkRegex = RegExpUtility.createRegExp( this._localNetworkRegexPattern, "XXXX", port + "" );
|
|
}
|
|
|
|
constructor( localAdressExtension:string, webAdress:string, disableForceHTTPS?:boolean, mode:PageHandlerMode = PageHandlerMode.PHP )
|
|
{
|
|
if ( disableForceHTTPS === true )
|
|
{
|
|
this._forceHTTPS = false;
|
|
}
|
|
|
|
if ( PageHandlerMode.HTML == mode || PageHandlerMode.ELECTRON == mode )
|
|
{
|
|
this._isHTMLMode = true;
|
|
}
|
|
|
|
if ( PageHandlerMode.ELECTRON == mode )
|
|
{
|
|
this.setFileLocation( localAdressExtension );
|
|
}
|
|
else
|
|
{
|
|
this.setAdress( localAdressExtension, webAdress );
|
|
}
|
|
|
|
//this._startPage = this.currentPage;
|
|
//console.log( "START PAGE:", this._startPage );
|
|
}
|
|
|
|
setFileLocation( location:string )
|
|
{
|
|
this._fileLocation = location;
|
|
this._isFileMode = true;
|
|
}
|
|
|
|
|
|
get localAdress()
|
|
{
|
|
let adress = this.trimmedLocation;
|
|
|
|
|
|
if ( adress.startsWith( PageHandler._localHost ) )
|
|
{
|
|
return PageHandler._localHost + this._localAdressExtension;
|
|
}
|
|
|
|
if ( PageHandler._localNetworkRegex.test( adress ) )
|
|
{
|
|
return PageHandler._localNetworkRegex.exec( adress )[ 1 ] + this._localAdressExtension;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
|
|
|
|
get isLocalNetwork()
|
|
{
|
|
let adress = this.trimmedLocation;
|
|
|
|
// console.log( "Checking adress for localhost", adress, PageHandler._localHost, PageHandler._localNetworkRegex );
|
|
|
|
if ( adress.startsWith( PageHandler._localHost ) )
|
|
{
|
|
console.log( "Is starting with localhost" );
|
|
return true;
|
|
}
|
|
|
|
// console.log( "Not starting with localhost" );
|
|
|
|
if ( PageHandler._localNetworkRegex.test( adress ) )
|
|
{
|
|
// console.log( "Is starting with localhost regex" );
|
|
return true;
|
|
}
|
|
|
|
// console.log( "Not starting with localhost regex" );
|
|
|
|
return false;
|
|
}
|
|
|
|
get isFileMode()
|
|
{
|
|
return this._isFileMode;
|
|
}
|
|
|
|
|
|
|
|
|
|
private _pagesLoaded = false;
|
|
|
|
async initialize()
|
|
{
|
|
this._isLoading = true;
|
|
|
|
let pagesPath = this.getAbsolutePath( this._pagesPath ) + "?" + new Date().getTime() ;
|
|
//console.log( "loading pages", pagesPath );
|
|
|
|
let pages:string[] = [];
|
|
|
|
setTimeout(
|
|
()=>
|
|
{
|
|
// console.log( "PAGES_DATA", ( window as any ).PAGES_DATA );
|
|
},
|
|
3000
|
|
);
|
|
|
|
if ( ( window as any ).PAGES_DATA )
|
|
{
|
|
//console.log( "USING PAGE DATA FROM JS PAGES" );
|
|
pages = ( window as any ).PAGES_DATA as string[];
|
|
}
|
|
else
|
|
{
|
|
console.log( "LOADING PAGE DATA FROM JSON", this.isLocalNetwork, pagesPath );
|
|
let pagesData = await Loader.loadJSON<PagesJSON>( pagesPath );
|
|
pages = pagesData.pages;
|
|
}
|
|
|
|
|
|
|
|
for ( let i = 0; i < pages.length; i++ )
|
|
{
|
|
let page = pages[ i ];
|
|
let absolutePath = this.getAbsolutePath( page );
|
|
//console.log( "adding page: ", page, ">>", absolutePath );
|
|
this._pages.set( page, new PageData( this, absolutePath, page ) );
|
|
}
|
|
|
|
this._pagesLoaded = true;
|
|
|
|
this._startPage = this.currentPage;
|
|
///console.log( "loading pages", this._startPage );
|
|
this.removeFunctionHandleLink();
|
|
|
|
this.resolveRootPaths( document );
|
|
|
|
this._isLoading = false;
|
|
|
|
this._startUpdater();
|
|
|
|
if ( this._followUpPage )
|
|
{
|
|
this._loadPage( this._followUpPage );
|
|
}
|
|
|
|
return Promise.resolve();
|
|
}
|
|
|
|
|
|
private _currentLanguage:string = null;
|
|
get currentLanguage()
|
|
{
|
|
if ( ! this._currentLanguage )
|
|
{
|
|
this._currentLanguage = document.querySelector( "html" ).getAttribute( "lang" ) || "en";
|
|
}
|
|
|
|
return this._currentLanguage;
|
|
}
|
|
|
|
get languageCode()
|
|
{
|
|
return this.currentLanguage as LanguageCode;
|
|
}
|
|
|
|
resolveRootPaths( target:Element|Document )
|
|
{
|
|
let filePath = this.currentPage;
|
|
// console.log( "resolveRootPaths: FILEPATH:" + filePath );
|
|
|
|
if ( filePath === null )
|
|
{
|
|
return;
|
|
}
|
|
|
|
this._rootPathResolver.process( target, filePath );
|
|
}
|
|
|
|
private async _loadPage( page:string ):Promise<void>
|
|
{
|
|
if ( this._isLoading )
|
|
{
|
|
this._followUpPage = page;
|
|
return Promise.resolve();
|
|
}
|
|
|
|
this._dispatchPageRequest( this._currentPage, page );
|
|
|
|
this._isLoading = true;
|
|
this._followUpPage = null;
|
|
|
|
this._nextPage = page;
|
|
let next = this._nextPage;
|
|
|
|
|
|
if ( this.transitionHandler )
|
|
{
|
|
await this.transitionHandler.onTransitionOut( this );
|
|
}
|
|
|
|
let pageData = await this._getPageData( next );
|
|
this._currentLanguage = pageData.language;
|
|
|
|
let elementType = new ElementType( this.pageRootTag );
|
|
let pageRoot = elementType.query( document.documentElement );
|
|
pageRoot.innerHTML = pageData.pageRootInnerHTML;
|
|
|
|
this.resolveRootPaths( pageRoot );
|
|
|
|
this._scrollToY( pageData.scrollTop );
|
|
document.title = pageData.title;
|
|
document.querySelector( "html" ).setAttribute( "lang", this._currentLanguage );
|
|
|
|
this._abortPreloadingImages = false;
|
|
|
|
if ( this._preloadImagesFlag )
|
|
{
|
|
await Promise.race( [ sleep( this._maxPreloadingDurationMS ), this._preloadImages() ])
|
|
}
|
|
|
|
this._abortPreloadingImages = true;
|
|
|
|
if ( this.transitionHandler )
|
|
{
|
|
await this.transitionHandler.onTransitionIn( this );
|
|
}
|
|
|
|
|
|
this._currentPage = this._nextPage;
|
|
|
|
if ( this._followUpPage )
|
|
{
|
|
let page = this._followUpPage;
|
|
this._followUpPage = null;
|
|
await this._loadPage( page );
|
|
}
|
|
|
|
this._isLoading = false;
|
|
|
|
return Promise.resolve();
|
|
}
|
|
|
|
|
|
private _pageAliases:Map<string,string> = new Map<string,string>();
|
|
|
|
public normalizePageLink( page:string )
|
|
{
|
|
return this._normalizePageLink( page );
|
|
}
|
|
|
|
private _normalizePageLink( page:string )
|
|
{
|
|
if ( page === "" )
|
|
{
|
|
return this._isHTMLMode ? "index.html" : "index.php";
|
|
}
|
|
|
|
if ( this._pages.has( page ) )
|
|
{
|
|
return page;
|
|
}
|
|
|
|
if ( this._pageAliases.has( page ) )
|
|
{
|
|
page = this._pageAliases.get( page );
|
|
}
|
|
|
|
if ( this._pages.has( page ) )
|
|
{
|
|
return page;
|
|
}
|
|
|
|
if ( ! this._isHTMLMode && page.endsWith( ".html" ) )
|
|
{
|
|
let phpPage = page.replace( /\.html$/, ".php" );
|
|
|
|
if ( this._pages.has( phpPage ) )
|
|
{
|
|
return phpPage;
|
|
}
|
|
}
|
|
|
|
let ending = this._isHTMLMode ? ".html" : ".php";
|
|
|
|
if ( ! page.endsWith( ending ) )
|
|
{
|
|
let indexPage = page + ending;
|
|
|
|
if ( this._pages.has( indexPage ) )
|
|
{
|
|
return indexPage;
|
|
}
|
|
|
|
let indexDirectoryPage = page.replace( /\/$/, "" ) + "/index" + ending;
|
|
|
|
if ( this._pages.has( indexDirectoryPage ) )
|
|
{
|
|
return indexDirectoryPage;
|
|
}
|
|
}
|
|
|
|
|
|
if ( PageHandler.isHiddenPage() )
|
|
{
|
|
return null;
|
|
}
|
|
|
|
// console.log( "Page not found:", page, [...this._pages.keys()].join( ", " ) );
|
|
return null;
|
|
}
|
|
|
|
static isHiddenPage()
|
|
{
|
|
let value = PageHandler.pageInfoAttribute.from( document.body );
|
|
return value === "hidden";
|
|
}
|
|
|
|
static readonly pageInfoAttribute = new ElementAttribute( "page-info" );
|
|
|
|
getFilteredPages( filter:RegExp )
|
|
{
|
|
let pages:string[] = [];
|
|
|
|
this._pages.forEach(
|
|
( v, k )=>
|
|
{
|
|
if ( filter.test( k ) )
|
|
{
|
|
pages.push( k );
|
|
}
|
|
}
|
|
);
|
|
|
|
return pages;
|
|
}
|
|
|
|
async getPage( page:string ):Promise<PageData>
|
|
{
|
|
console.log( "getPage:", page );
|
|
return this._getPageData( page );
|
|
}
|
|
|
|
private async _getPageData( page:string ):Promise<PageData>
|
|
{
|
|
|
|
if ( ! this._pages.has( page ) )
|
|
{
|
|
return Promise.resolve( null );
|
|
}
|
|
|
|
let data = this._pages.get( page );
|
|
|
|
if ( ! data.ready )
|
|
{
|
|
await data.load();
|
|
}
|
|
|
|
|
|
return Promise.resolve( data );
|
|
}
|
|
|
|
private _abortPreloadingImages = false;
|
|
|
|
private async _preloadImages():Promise<void>
|
|
{
|
|
|
|
let images = ElementType.image.queryAll( document.body );
|
|
let backgroundImages = document.body.querySelectorAll( `[style*="background-image"]` );
|
|
|
|
//console.log( "preloading images", `found: <img> ${images.length}, [style*="background-image"] ${backgroundImages.length}`, )
|
|
|
|
for ( let i = 0; i < images.length && ! this._abortPreloadingImages; i++ )
|
|
{
|
|
let path = images[ i ].getAttribute( "src" );
|
|
|
|
if ( path && path !== "" )
|
|
{
|
|
await this._preloadImage( path );
|
|
}
|
|
|
|
}
|
|
|
|
for ( let i = 0; i < backgroundImages.length && ! this._abortPreloadingImages; i++ )
|
|
{
|
|
let htmlElement = backgroundImages[ i ] as HTMLElement;
|
|
let backgroundImageValue = htmlElement.style.backgroundImage;
|
|
var regexResult = backgroundImageValue.match(/url\(["']?([^"']*)["']?\)/);
|
|
|
|
if ( regexResult && regexResult[ 1 ] )
|
|
{
|
|
let path = regexResult[ 1 ];
|
|
await this._preloadImage( path );
|
|
}
|
|
|
|
}
|
|
|
|
return Promise.resolve();
|
|
|
|
|
|
}
|
|
|
|
private async _preloadImage( path:string ):Promise<void>
|
|
{
|
|
try
|
|
{
|
|
//console.log( "preloading image", path );
|
|
await Loader.loadImage( path );
|
|
}
|
|
catch ( e )
|
|
{
|
|
|
|
}
|
|
|
|
|
|
return Promise.resolve();
|
|
}
|
|
|
|
|
|
getAbsolutePath( path:string )
|
|
{
|
|
if ( this.isFileMode )
|
|
{
|
|
return this._fileLocation + "/" + path
|
|
}
|
|
else if ( this.isLocalNetwork )
|
|
{
|
|
return this.localAdress + "/" + path
|
|
}
|
|
else
|
|
{
|
|
return this._webAdress + "/" + path;
|
|
}
|
|
}
|
|
|
|
getRootPath( absolutePath:string )
|
|
{
|
|
let pagePath:string = null;
|
|
|
|
if ( this.isFileMode )
|
|
{
|
|
pagePath = absolutePath.substring( this._fileLocation.length );
|
|
}
|
|
else if ( this.isLocalNetwork )
|
|
{
|
|
pagePath = absolutePath.substring( this.localAdress.length );
|
|
}
|
|
else
|
|
{
|
|
if ( absolutePath.startsWith( "http:" ) && this._forceHTTPS )
|
|
{
|
|
absolutePath = "https:" + absolutePath.substring( "http:".length );
|
|
}
|
|
|
|
pagePath = absolutePath.substring( this._webAdress.length );
|
|
}
|
|
|
|
if ( pagePath.startsWith( "/" ) )
|
|
{
|
|
pagePath = pagePath.substring( 1 );
|
|
}
|
|
|
|
return pagePath;
|
|
}
|
|
|
|
|
|
|
|
get trimmedLocation()
|
|
{
|
|
let trimmedLocation = window.location.href;
|
|
|
|
let hash = /#.+$/.exec( trimmedLocation );
|
|
|
|
if ( hash )
|
|
{
|
|
trimmedLocation = trimmedLocation.replace( /#.+$/, "" );
|
|
}
|
|
|
|
let search = /\?.+$/.exec( trimmedLocation );
|
|
|
|
if ( search )
|
|
{
|
|
trimmedLocation = trimmedLocation.replace( /\?.+$/, "" );
|
|
}
|
|
|
|
return trimmedLocation;
|
|
}
|
|
|
|
get hasFunctionHandleLink()
|
|
{
|
|
let trimmedLocation = window.location.href;
|
|
|
|
let hash = /#\[.+\]$/.exec( trimmedLocation );
|
|
|
|
if ( hash )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
removeFunctionHandleLink()
|
|
{
|
|
if ( this.hasFunctionHandleLink )
|
|
{
|
|
window.location.href = this.trimmedLocation;
|
|
}
|
|
}
|
|
|
|
get currentPage()
|
|
{
|
|
let location = this.getRootPath( this.trimmedLocation );
|
|
|
|
let normalized = this._normalizePageLink( location );
|
|
|
|
return normalized;
|
|
}
|
|
|
|
private _startUpdater()
|
|
{
|
|
if ( 'scrollRestoration' in window.history)
|
|
{
|
|
window.history.scrollRestoration = 'manual';
|
|
}
|
|
|
|
let page = this.currentPage;
|
|
|
|
this._currentPage = page;
|
|
this._nextPage = page;
|
|
|
|
let updater = () =>
|
|
{
|
|
requestAnimationFrame(
|
|
()=>
|
|
{
|
|
this._update();
|
|
updater();
|
|
}
|
|
);
|
|
}
|
|
|
|
updater();
|
|
}
|
|
|
|
|
|
|
|
get currentScrollTop()
|
|
{
|
|
var value= typeof window.pageYOffset !== undefined ? window.pageYOffset:
|
|
document.documentElement.scrollTop? document.documentElement.scrollTop:
|
|
document.body.scrollTop? document.body.scrollTop:0;
|
|
return value;
|
|
}
|
|
|
|
|
|
|
|
replaceLinks()
|
|
{
|
|
let anchors = ElementType.anchor.queryAll( document.body );
|
|
|
|
for ( let a of anchors )
|
|
{
|
|
this._processLink( a as HTMLAnchorElement );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private _dispatchPageRequest( lastPage:string, nextPage:string )
|
|
{
|
|
let pageRequestEvent = new PageRequestEvent();
|
|
pageRequestEvent.lastPage = lastPage;
|
|
pageRequestEvent.nextPage = nextPage;
|
|
|
|
this.onPageRequest.dispatch( pageRequestEvent );
|
|
}
|
|
|
|
createAbsoluteLink( link:Link )
|
|
{
|
|
let clone = link.clone();
|
|
|
|
if ( LinkType.ABSOLUTE === link.type )
|
|
{
|
|
return clone;
|
|
}
|
|
|
|
clone.type = LinkType.ABSOLUTE;
|
|
|
|
if ( LinkType.ROOT === link.type )
|
|
{
|
|
clone.path = link.path.replace( "::", window.location.host );
|
|
}
|
|
|
|
if ( LinkType.RELATIVE === link.type )
|
|
{
|
|
clone.path = new URL( link.path, window.location.href ).href;
|
|
}
|
|
|
|
|
|
return clone;
|
|
}
|
|
|
|
createRootLink( link:Link )
|
|
{
|
|
if ( link.type === LinkType.ROOT )
|
|
{
|
|
return link.clone();
|
|
}
|
|
|
|
let absoluteLink = this.createAbsoluteLink( link );
|
|
|
|
let clone = absoluteLink.clone();
|
|
clone.type = LinkType.ROOT;
|
|
clone.path = RootPathResolver.rootToken + ( new URL( absoluteLink.path ).pathname );
|
|
|
|
return clone;
|
|
}
|
|
|
|
getRealPagePath( page:string )
|
|
{
|
|
page = page.replace( /^\//, "" );
|
|
|
|
if ( this._pages.has( page ) )
|
|
{
|
|
return page;
|
|
}
|
|
|
|
let phpExtensionPage = page + ".php";
|
|
|
|
if ( this._pages.has( phpExtensionPage ) )
|
|
{
|
|
return page;
|
|
}
|
|
|
|
let directoryWithIndexPage = page + "/index.php";
|
|
|
|
if ( this._pages.get( directoryWithIndexPage ) )
|
|
{
|
|
return directoryWithIndexPage;
|
|
}
|
|
|
|
console.warn( "Page not found:", page );
|
|
|
|
return null;
|
|
}
|
|
|
|
createRelativeLink( link:Link )
|
|
{
|
|
if ( link.type === LinkType.RELATIVE )
|
|
{
|
|
return link.clone();
|
|
}
|
|
|
|
|
|
let clone = this.createRootLink( link ).clone();
|
|
clone.type = LinkType.RELATIVE;
|
|
|
|
let replaced = link.path.replace( RootPathResolver.rootToken, "" );
|
|
let file = RegExpUtility.fileNameOrLastPath( replaced );
|
|
|
|
let pathDirectory = RegExpUtility.parentPath( this._normalizePageLink( replaced ) );
|
|
let currentDirectory = this.currentPath;
|
|
|
|
let isDirectoryWithoutSlash = false;
|
|
let directoryPrefix = "";
|
|
|
|
|
|
if ( ! window.location.pathname.endsWith( "/" ) )
|
|
{
|
|
let windowPath = window.location.pathname.replace( /^\//, "" );
|
|
let windowWithoutSlashPath = windowPath + "/index.php";
|
|
let normalizedPath = this._normalizePageLink( windowPath );
|
|
|
|
isDirectoryWithoutSlash = normalizedPath === windowWithoutSlashPath;
|
|
|
|
console.log( "CREATED RELATIVE INFO", windowPath, normalizedPath, isDirectoryWithoutSlash );
|
|
|
|
if ( isDirectoryWithoutSlash )
|
|
{
|
|
directoryPrefix = RegExpUtility.fileNameOrLastPath( windowPath );
|
|
}
|
|
}
|
|
|
|
let path = RegExpUtility.createRelativeDirectoryPath( currentDirectory, pathDirectory );
|
|
|
|
if ( path != "" && ! path.endsWith( "/" ) )
|
|
{
|
|
path += "/";
|
|
}
|
|
|
|
path += file;
|
|
|
|
if ( isDirectoryWithoutSlash )
|
|
{
|
|
path = directoryPrefix + "/" + path;
|
|
}
|
|
|
|
console.log(
|
|
"CREATED RELATIVE LINK:", link.path,
|
|
">> from ", currentDirectory, isDirectoryWithoutSlash, "to", pathDirectory, ">>",
|
|
path );
|
|
|
|
|
|
|
|
|
|
|
|
clone.path = path;
|
|
|
|
return clone;
|
|
}
|
|
|
|
|
|
|
|
convertLink( link:Link, type:LinkType )
|
|
{
|
|
if ( link.type === type )
|
|
{
|
|
return link.clone();
|
|
}
|
|
|
|
if ( LinkType.ABSOLUTE == type )
|
|
{
|
|
return this.createAbsoluteLink( link );
|
|
}
|
|
|
|
if ( LinkType.ROOT == type )
|
|
{
|
|
return this.createRootLink( link );
|
|
}
|
|
|
|
if ( LinkType.RELATIVE == type )
|
|
{
|
|
return this.createRelativeLink( link );
|
|
}
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
setPage( href:string, loadPage:boolean = true )
|
|
{
|
|
let normalizedPath:string = null;
|
|
|
|
if ( RootPathResolver.isRootPath( href ) )
|
|
{
|
|
let resolvedRootPath = RootPathResolver.clearRootPath( href );
|
|
normalizedPath = this._normalizePageLink( resolvedRootPath );
|
|
this._pages.get( normalizedPath ).scrollTop = 0;
|
|
console.log( "LOADING ROOT ADRESS", { href, resolvedRootPath, normalizedPath } );
|
|
|
|
let sourceDirectory = this.currentPath;
|
|
let targetDirectory = RegExpUtility.parentPath( normalizedPath );
|
|
let relativePath = RegExpUtility.createRelativeDirectoryPath( sourceDirectory, targetDirectory );
|
|
let targetFile = RegExpUtility.fileNameOrLastPath( normalizedPath );
|
|
let historyAdress = "." + RegExpUtility.join( relativePath, targetFile );
|
|
|
|
let historyAdressShort = historyAdress.replace( /\/?index\.php$/, "" );
|
|
historyAdressShort = historyAdressShort.replace( /\.php$/, "" );
|
|
|
|
console.log( "PUSHING STATE", historyAdress, historyAdressShort );
|
|
history.pushState( { historyAdressShort }, "", historyAdressShort );
|
|
|
|
if ( ! loadPage )
|
|
{
|
|
this._nextPage = historyAdressShort;
|
|
this._followUpPage = null;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
let pageAdress = this.resolveRelativeLink( href );
|
|
normalizedPath = this._normalizePageLink( pageAdress );
|
|
console.log( "LOADING RELATIVE ADRESS", { href, pageAdress, normalizedPath } );
|
|
this._pages.get( normalizedPath ).scrollTop = 0;
|
|
history.pushState( { href }, "", href );
|
|
|
|
if ( ! loadPage )
|
|
{
|
|
this._nextPage = href;
|
|
this._followUpPage = null;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
static pageHandlerLink = new ElementAttribute( "page-handler-link" );
|
|
|
|
makePageHandleLink( a:HTMLAnchorElement )
|
|
{
|
|
let originalHref = ElementAttribute.href.from( a, "" );
|
|
|
|
if ( originalHref.startsWith( "https://" ) || originalHref.startsWith( "https://" ) )
|
|
{
|
|
AttributeValue.target_blank.set( a );
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
AttributeValue.target_blank.removeFrom( a );
|
|
}
|
|
|
|
if ( PageHandler.pageHandlerLink.in( a ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
PageHandler.pageHandlerLink.to( a );
|
|
|
|
|
|
let pageHandlingAttribute = "data-page-handling";
|
|
|
|
a.addEventListener(
|
|
"mousedown",
|
|
( e:MouseEvent )=>
|
|
{
|
|
let hasRequest = e.button === 0;
|
|
a.setAttribute( pageHandlingAttribute, hasRequest + "" );
|
|
}
|
|
);
|
|
|
|
a.addEventListener(
|
|
"click",
|
|
( e:MouseEvent)=>
|
|
{
|
|
|
|
let href = a.getAttribute( "href" );
|
|
|
|
let hasRequest = a.getAttribute( pageHandlingAttribute ) === "true";
|
|
|
|
if ( hasRequest )
|
|
{
|
|
a.removeAttribute( "href" );
|
|
|
|
/*
|
|
let pageAdress = this.resolveRelativeLink( href );
|
|
pageAdress = this._normalizePageLink( pageAdress );
|
|
|
|
console.log( "href", `"${href}"`, "pageAdress", `"${pageAdress}"` );
|
|
|
|
|
|
this._pages.get( pageAdress ).scrollTop = 0;
|
|
history.pushState( { href }, "", href );
|
|
*/
|
|
|
|
this.setPage( href );
|
|
|
|
e.preventDefault();
|
|
e.stopImmediatePropagation();
|
|
e.stopPropagation();
|
|
|
|
setTimeout(
|
|
()=>
|
|
{
|
|
a.setAttribute( "href", href );
|
|
},
|
|
200
|
|
)
|
|
}
|
|
|
|
|
|
}
|
|
)
|
|
}
|
|
|
|
static isFunctionHandleLink( link:string )
|
|
{
|
|
return link.startsWith( "#[" ) && link.endsWith( "]" );
|
|
}
|
|
|
|
static makeFunctionHandleLink( a:HTMLAnchorElement )
|
|
{
|
|
let element = a as FunctionHandleAnchor;
|
|
|
|
if ( element._assignedClickListener )
|
|
{
|
|
return;
|
|
}
|
|
|
|
element._assignedClickListener = true;
|
|
|
|
let hrefValue = ElementAttribute.href.from( a );
|
|
|
|
OnMouseDown.add( a, ( e:MouseEvent )=>
|
|
{
|
|
ElementAttribute.href.removeFrom( a );
|
|
}
|
|
);
|
|
|
|
OnMouseEnter.add( a, ( e:MouseEvent )=>
|
|
{
|
|
ElementAttribute.href.to( a, hrefValue );
|
|
}
|
|
);
|
|
|
|
/*
|
|
OnContextMenu.add( a, ( e:MouseEvent )=>
|
|
{
|
|
e.preventDefault();
|
|
e.stopImmediatePropagation();
|
|
e.stopPropagation();
|
|
}
|
|
);
|
|
*/
|
|
|
|
OnClick.add( a, ( e:MouseEvent)=>
|
|
{
|
|
console.log( "FUNCTION HANDLE CLICK", e );
|
|
e.preventDefault();
|
|
e.stopImmediatePropagation();
|
|
e.stopPropagation();
|
|
|
|
if ( element._functionCallback )
|
|
{
|
|
element._functionCallback();
|
|
}
|
|
|
|
setTimeout(
|
|
()=>
|
|
{
|
|
ElementAttribute.href.to( a, hrefValue );
|
|
},
|
|
200
|
|
)
|
|
}
|
|
)
|
|
}
|
|
|
|
static setFunctionHandleLink( e:Element, name:string, callback:()=>void )
|
|
{
|
|
let a = e as HTMLAnchorElement;
|
|
this.setFunctionHandleName( a, name );
|
|
this.setFunctionHandleCallback( a, callback );
|
|
this.makeFunctionHandleLink( a );
|
|
|
|
}
|
|
|
|
static setFunctionHandleName( a:HTMLAnchorElement, name:string )
|
|
{
|
|
ElementAttribute.href.to( a, `#[ ${name} ]`);
|
|
}
|
|
|
|
static setFunctionHandleCallback( a:HTMLAnchorElement, callback:()=>void )
|
|
{
|
|
let handle = a as FunctionHandleAnchor;
|
|
handle._functionCallback = callback;
|
|
}
|
|
|
|
|
|
private _processLink( a:HTMLAnchorElement )
|
|
{
|
|
let hrefValue = ElementAttribute.href.from( a );
|
|
|
|
if ( ! hrefValue )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( hrefValue.startsWith( "http" ) || hrefValue.startsWith( "mailto" ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( PageHandler.isFunctionHandleLink( hrefValue ) )
|
|
{
|
|
PageHandler.makeFunctionHandleLink( a );
|
|
return;
|
|
}
|
|
|
|
if ( AttributeValue.target_blank.in( a ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
this.makePageHandleLink( a );
|
|
|
|
}
|
|
|
|
private get currentPath():string
|
|
{
|
|
let relativePagePath = this.getRootPath( this.trimmedLocation );
|
|
let currentPath = this._normalizePageLink( relativePagePath );
|
|
|
|
if ( currentPath.endsWith( ".html" ) || currentPath.endsWith( ".php" ) )
|
|
{
|
|
let end = currentPath.lastIndexOf( "/" );
|
|
|
|
if ( end === -1 )
|
|
{
|
|
console.log( "No slash in path", currentPath );
|
|
return "";
|
|
|
|
}
|
|
|
|
currentPath = currentPath.substring( 0, end );
|
|
}
|
|
|
|
if ( currentPath.startsWith( "/" ) )
|
|
{
|
|
currentPath = currentPath.substring( 1 );
|
|
}
|
|
|
|
return currentPath;
|
|
}
|
|
|
|
private resolveRelativeLink( link:string )
|
|
{
|
|
let currentPath = this.currentPath;
|
|
|
|
if ( ! window.location.pathname.endsWith( "/" ) )
|
|
{
|
|
let windowPath = window.location.pathname.replace( /^\//, "" );
|
|
let windowExtendedPath = windowPath + "/index.php";
|
|
let normalizedPath = this._normalizePageLink( windowPath );
|
|
|
|
if ( normalizedPath === windowExtendedPath )
|
|
{
|
|
currentPath = RegExpUtility.parentPath( currentPath );
|
|
|
|
console.log( "CREATED SHORTER PATH:", this.currentPath, ">>", currentPath );
|
|
}
|
|
else
|
|
{
|
|
console.log( "CREATED PATH IS OK:", { windowPath, windowExtendedPath, normalizedPath, currentPath } );
|
|
}
|
|
|
|
|
|
}
|
|
|
|
let pathFragments = currentPath === "" ? [] : currentPath.split( "/" );
|
|
|
|
let linkPathFragments = link.split( "/" );
|
|
|
|
for ( let i = 0; i < linkPathFragments.length; i++ )
|
|
{
|
|
if ( linkPathFragments[ i ] === ".." )
|
|
{
|
|
pathFragments.pop();
|
|
}
|
|
else
|
|
{
|
|
pathFragments.push( linkPathFragments[ i ] );
|
|
}
|
|
}
|
|
|
|
let resolvedPath = pathFragments.join( "/" );
|
|
console.log( "resolving: ", this.currentPath, link, ">>", resolvedPath );
|
|
return resolvedPath;
|
|
}
|
|
|
|
setPageWithoutLoading( rootPath:string )
|
|
{
|
|
//this._nextPage = page;
|
|
//this._followUpPage = page;
|
|
//this.loadPage( page );
|
|
|
|
rootPath = rootPath.replace( /\.php$/, "" );
|
|
|
|
let page = "::/" + rootPath;
|
|
let relativeLink = this.createRelativeLink( Link.Root( page ) );
|
|
let relative = relativeLink.path;
|
|
this._nextPage = relativeLink.path;
|
|
|
|
console.log( "setPageWithoutLoading", { relative, page, rootPath } );
|
|
this.setPage( relative, false );
|
|
}
|
|
|
|
getRootPathNextPage()
|
|
{
|
|
let link = Link.Relative( this._nextPage );
|
|
let abs = this.createRootLink( link );
|
|
|
|
abs.path = abs.path.replace( /^\:\:\/\//, "" );
|
|
|
|
//console.log( link.path, abs.path );
|
|
return this._normalizePageLink( abs.path );
|
|
}
|
|
|
|
private _update()
|
|
{
|
|
if ( this._isLoading )
|
|
{
|
|
return;
|
|
}
|
|
|
|
let location = this.getRootPath( this.trimmedLocation );
|
|
|
|
location = this._normalizePageLink( location );
|
|
|
|
let rootPathNextPage = this.getRootPathNextPage();
|
|
|
|
if ( rootPathNextPage !== location && this._nextPage !== location )
|
|
{
|
|
console.log(
|
|
{
|
|
current: location,
|
|
next: this._nextPage,
|
|
normalizedNext: rootPathNextPage
|
|
}
|
|
);
|
|
|
|
this._loadPage( location );
|
|
}
|
|
else
|
|
{
|
|
let pageData = this._pages.get( this._currentPage );
|
|
|
|
if ( pageData )
|
|
{
|
|
pageData.scrollTop = this.currentScrollTop;
|
|
}
|
|
else
|
|
{
|
|
if ( ! PageHandler.isHiddenPage() )
|
|
{
|
|
console.log( this._currentPage, this.currentPage, "NO PAGE DATA" );
|
|
}
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
private _scrollToY( y:number )
|
|
{
|
|
document.documentElement.scrollTop = y;
|
|
document.body.scrollTop = y;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} |