You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

574 lines
13 KiB

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>three.js docs</title>
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<link rel="shortcut icon" href="../files/favicon_white.ico" media="(prefers-color-scheme: dark)"/>
<link rel="shortcut icon" href="../files/favicon.ico" media="(prefers-color-scheme: light)" />
<link rel="stylesheet" type="text/css" href="../files/main.css">
<!-- console sandbox -->
<script src="../build/three.min.js" async defer></script>
</head>
<body>
<div id="panel">
<div id="header">
<h1><a href="https://threejs.org">three.js</a></h1>
<div id="sections">
<span class="selected">docs</span>
<a href="../examples/#webgl_animation_keyframes">examples</a>
</div>
<div id="expandButton"></div>
</div>
<div id="panelScrim"></div>
<div id="contentWrapper">
<div id="inputWrapper">
<input placeholder="" type="text" id="filterInput" autocorrect="off" autocapitalize="off" spellcheck="false" />
<div id="exitSearchButton"></div>
<select id="language">
<option value="en">en</option>
<option value="ar">ar</option>
<option value="ko">한국어</option>
<option value="zh">中文</option>
<option value="ja">日本語</option>
<option value="it">it</option>
<option value="pt-br">pt-br</option>
<option value="fr">fr</option>
<option value="ru">ru</option>
</select>
</div>
<div id="content"></div>
</div>
</div>
<iframe name="viewer"></iframe>
<script>
const panel = document.getElementById( 'panel' );
const content = document.getElementById( 'content' );
const expandButton = document.getElementById( 'expandButton' );
const exitSearchButton = document.getElementById( 'exitSearchButton' );
const panelScrim = document.getElementById( 'panelScrim' );
const filterInput = document.getElementById( 'filterInput' );
let iframe = document.querySelector( 'iframe' );
const sectionLink = document.querySelector( '#sections > a' );
const sectionDefaultHref = sectionLink.href;
const pageProperties = {};
const titles = {};
const categoryElements = [];
let navigation;
init();
async function init() {
const list = await ( await fetch( 'list.json' ) ).json();
// *BufferGeometry to *Geometry
if ( /Instanced/.exec( window.location.hash ) === null && /([\w]+)BufferGeometry$/.exec( window.location.hash ) ) {
window.location.hash = window.location.hash.replace( 'BufferGeometry', 'Geometry' );
}
const hash = window.location.hash.substring( 1 );
// Localisation
let language = 'en';
if ( /^(api|manual|examples)/.test( hash ) ) {
const hashLanguage = /^(api|manual|examples)\/(en|ar|ko|zh|ja|it|pt-br|fr|ru)\//.exec( hash );
if ( hashLanguage === null ) {
// Route old non-localised api links
window.location.hash = hash.replace( /^(api|manual|examples)/, '$1/en' );
} else {
language = hashLanguage[ 2 ];
}
}
const languageSelect = document.getElementById( 'language' );
languageSelect.value = language;
languageSelect.addEventListener( 'change', function () {
setLanguage( this.value );
} );
function setLanguage( value ) {
language = value;
createNavigation( list, language );
updateFilter();
autoChangeUrlLanguage( language );
}
// Functionality for hamburger button (on small devices)
expandButton.onclick = function ( event ) {
event.preventDefault();
panel.classList.toggle( 'open' );
};
panelScrim.onclick = function ( event ) {
event.preventDefault();
panel.classList.toggle( 'open' );
};
// Functionality for search/filter input field
filterInput.onfocus = function () {
panel.classList.add( 'searchFocused' );
};
filterInput.onblur = function () {
if ( filterInput.value === '' ) {
panel.classList.remove( 'searchFocused' );
}
};
filterInput.oninput = function () {
updateFilter();
};
exitSearchButton.onclick = function () {
filterInput.value = '';
updateFilter();
panel.classList.remove( 'searchFocused' );
};
// Activate content and title change on browser navigation
window.onpopstate = createNewIframe;
// Create the navigation panel and configure the iframe
createNavigation( list, language );
createNewIframe();
// Handle search query
filterInput.value = extractQuery();
if ( filterInput.value !== '' ) {
panel.classList.add( 'searchFocused' );
updateFilter();
} else {
updateLink( '' );
}
}
// Navigation Panel
function createLink( pageName, pageURL ) {
const link = document.createElement( 'a' );
link.href = pageURL + '.html';
link.textContent = pageName;
link.setAttribute( 'target', 'viewer' );
link.addEventListener( 'click', function ( event ) {
if ( event.button !== 0 || event.ctrlKey || event.altKey || event.metaKey ) return;
window.location.hash = pageURL;
panel.classList.remove( 'open' );
content.querySelectorAll( 'a' ).forEach( function ( item ) {
item.classList.remove( 'selected' );
} );
link.classList.add( 'selected' );
} );
return link;
}
function createNavigation( list, language ) {
if ( navigation !== undefined ) {
content.removeChild( navigation );
}
// Create the navigation panel using data from list.js
navigation = document.createElement( 'div' );
content.appendChild( navigation );
if ( language === 'ar' ) {
navigation.style.direction = 'rtl';
}
const localList = list[ language ];
const selectedPage = window.location.hash.substring( 1 );
for ( const section in localList ) {
// Create sections
const categories = localList[ section ];
const sectionHead = document.createElement( 'h2' );
sectionHead.textContent = section;
navigation.appendChild( sectionHead );
for ( const category in categories ) {
// Create categories
const pages = categories[ category ];
const categoryContainer = document.createElement( 'div' );
navigation.appendChild( categoryContainer );
const categoryHead = document.createElement( 'h3' );
categoryHead.textContent = category;
categoryContainer.appendChild( categoryHead );
const categoryContent = document.createElement( 'ul' );
categoryContainer.appendChild( categoryContent );
for ( const pageName in pages ) {
// Create page links
const pageURL = pages[ pageName ];
// Localisation
const listElement = document.createElement( 'li' );
categoryContent.appendChild( listElement );
const linkElement = createLink( pageName, pageURL );
listElement.appendChild( linkElement );
// select current page
if ( pageURL === selectedPage ) {
linkElement.classList.add( 'selected' );
}
// Gather the main properties for the current subpage
pageProperties[ pageName ] = {
section: section,
category: category,
pageURL: pageURL,
linkElement: linkElement
};
// Gather the document titles (used for easy access on browser navigation)
titles[ pageURL ] = pageName;
}
// Gather the category elements for easy access on filtering
categoryElements.push( categoryContent );
}
}
}
// Auto change language url. If a reader open a document in English, when they click "zh", the document they read will auto change into Chinese version
function autoChangeUrlLanguage( language ) {
const hash = location.hash;
if ( hash === '' ) return;
const docType = hash.slice( 0, hash.indexOf( '/' ) + 1 );
let docLink = hash.slice( hash.indexOf( '/' ) + 1 );
docLink = docLink.slice( docLink.indexOf( '/' ) );
location.href = docType + language + docLink;
}
// Filtering
function extractQuery() {
const search = window.location.search;
if ( search.indexOf( '?q=' ) !== - 1 ) {
return decodeURI( search.slice( 3 ) );
}
return '';
}
function escapeRegExp( string ) {
return string.replace( /[.*+?^${}()|[\]\\]/g, '\\$&' ); // https://stackoverflow.com/a/6969486/5250847
}
function updateFilter() {
let v = filterInput.value.trim();
v = v.replace( /\s+/gi, ' ' ); // replace multiple whitespaces with a single one
if ( v !== '' ) {
window.history.replaceState( {}, '', '?q=' + v + window.location.hash );
} else {
window.history.replaceState( {}, '', window.location.pathname + window.location.hash );
}
//
const regExp = new RegExp( escapeRegExp( v ), 'gi' );
for ( let pageName in pageProperties ) {
const linkElement = pageProperties[ pageName ].linkElement;
const categoryClassList = linkElement.parentElement.classList;
const filterResults = pageName.match( regExp );
if ( filterResults !== null && filterResults.length > 0 ) {
pageName = pageName.replaceAll( regExp, '<b>$&</b>' );
categoryClassList.remove( 'hidden' );
linkElement.innerHTML = pageName;
} else {
// Hide all non-matching page names
categoryClassList.add( 'hidden' );
}
}
displayFilteredPanel();
updateLink( v );
}
function updateLink( search ) {
// update examples link
if ( search ) {
const link = sectionLink.href.split( /[?#]/ )[ 0 ];
sectionLink.href = `${link}?q=${search}`;
} else {
sectionLink.href = sectionDefaultHref;
}
}
function displayFilteredPanel() {
// Show/hide categories depending on their content
// First check if at least one page in this category is not hidden
categoryElements.forEach( function ( category ) {
const pages = category.children;
const pagesLength = pages.length;
const sectionClassList = category.parentElement.classList;
let hideCategory = true;
for ( let i = 0; i < pagesLength; i ++ ) {
const pageClassList = pages[ i ].classList;
if ( ! pageClassList.contains( 'hidden' ) ) {
hideCategory = false;
}
}
// If and only if all page names are hidden, hide the whole category
if ( hideCategory ) {
sectionClassList.add( 'hidden' );
} else {
sectionClassList.remove( 'hidden' );
}
} );
}
// Routing
function setUrlFragment( pageName ) { // eslint-disable-line no-unused-vars
// Handle navigation from the subpages (iframes):
// First separate the member (if existing) from the page name,
// then identify the subpage's URL and set it as URL fragment (re-adding the member)
const splitPageName = decomposePageName( pageName, '.', '.' );
const currentProperties = pageProperties[ splitPageName[ 0 ] ];
if ( currentProperties ) {
window.location.hash = currentProperties.pageURL + splitPageName[ 1 ];
createNewIframe();
}
}
function createNewIframe() {
// Change the content displayed in the iframe
// First separate the member part of the fragment (if existing)
const hash = window.location.hash.substring( 1 );
const splitHash = decomposePageName( hash, '.', '#' );
// Creating a new Iframe instead of assigning a new src is
// a cross-browser solution to allow normal browser navigation;
// - only assigning a new src would result in two history states each time.
// Note: iframe.contentWindow.location.replace(hash) should work, too,
// but it doesn't work in Edge with links from the subpages!
let subtitle;
const oldIframe = iframe;
iframe = oldIframe.cloneNode();
if ( hash && titles[ splitHash[ 0 ] ] ) {
iframe.src = splitHash[ 0 ] + '.html' + splitHash[ 1 ];
subtitle = titles[ splitHash[ 0 ] ] + splitHash[ 1 ] + ' – ';
iframe.style.display = 'unset';
} else {
iframe.src = '';
subtitle = '';
iframe.style.display = 'none';
}
document.body.replaceChild( iframe, oldIframe );
document.title = subtitle + 'three.js docs';
}
function decomposePageName( pageName, oldDelimiter, newDelimiter ) {
// Helper function for separating the member (if existing) from the pageName
// For example: 'Geometry.morphTarget' can be converted to
// ['Geometry', '.morphTarget'] or ['Geometry', '#morphTarget']
// Note: According RFC 3986 no '#' allowed inside of an URL fragment!
let parts = [];
const dotIndex = pageName.indexOf( oldDelimiter );
if ( dotIndex !== - 1 ) {
parts = pageName.split( oldDelimiter );
parts[ 1 ] = newDelimiter + parts[ 1 ];
} else {
parts[ 0 ] = pageName;
parts[ 1 ] = '';
}
return parts;
}
//
console.log( [
' __ __',
' __/ __\\ / __\\__ ____ _____ _____',
'/ __/ /\\/ / /___\\/ ____\\/ _____\\/ _____\\',
'\\/_ __/ / _ / / __/ / __ / / __ /_ __ _____',
'/ / / / / / / / / / / / ___/ / ___/\\ _\\/ __\\/ _____\\',
'\\/__/ \\/__/\\/__/\\/__/ \\/_____/\\/_____/\\/__/ / / / ___/',
' / __/ / \\__ \\',
' \\/____/\\/_____/'
].join( '\n' ) );
</script>
</body>
</html>