Some ideas on API

probably not going to continue, as there is nowhere near enough need for
it
This commit is contained in:
2025-10-24 14:29:51 +02:00
parent ef80cf45dc
commit 77a83f6a4e
9 changed files with 937 additions and 969 deletions

1496
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "impress.js", "name": "impress.js",
"version": "1.1.0", "version": "3.0.0",
"description": "It's a presentation framework based on the power of CSS3 transforms and transitions in modern browsers and inspired by the idea behind prezi.com.", "description": "It's a presentation framework based on the power of CSS3 transforms and transitions in modern browsers and inspired by the idea behind prezi.com.",
"main": "js/impress.js", "main": "js/impress.js",
"repository": { "repository": {

View File

@@ -1,75 +1,75 @@
const fs = require('fs'); const fs = require( 'fs' );
var ls = require('ls'); var ls = require( 'ls' );
var path = require('path'); var path = require( 'path' );
var Terser = require("terser"); var Terser = require( 'terser' );
var files = ['src/impress.js']; var files = ['src/impress.js'];
// Libraries from src/lib // Libraries from src/lib
files.push('src/lib/gc.js', 'src/lib/util.js', 'src/lib/rotation.js') files.push( 'src/lib/gc.js', 'src/lib/util.js', 'src/lib/rotation.js' );
// Plugins from src/plugins // Plugins from src/plugins
files.push('src/plugins/autoplay/autoplay.js', files.push(
'src/plugins/blackout/blackout.js', 'src/plugins/autoplay/autoplay.js',
'src/plugins/extras/extras.js', 'src/plugins/blackout/blackout.js',
'src/plugins/form/form.js', 'src/plugins/extras/extras.js',
'src/plugins/fullscreen/fullscreen.js', 'src/plugins/form/form.js',
'src/plugins/goto/goto.js', 'src/plugins/fullscreen/fullscreen.js',
'src/plugins/bookmark/bookmark.js', 'src/plugins/goto/goto.js',
'src/plugins/help/help.js', 'src/plugins/bookmark/bookmark.js',
'src/plugins/impressConsole/impressConsole.js', 'src/plugins/help/help.js',
'src/plugins/media/media.js', 'src/plugins/impressConsole/impressConsole.js',
'src/plugins/mobile/mobile.js', 'src/plugins/media/media.js',
'src/plugins/mouse-timeout/mouse-timeout.js', 'src/plugins/mobile/mobile.js',
'src/plugins/navigation/navigation.js', 'src/plugins/mouse-timeout/mouse-timeout.js',
'src/plugins/navigation-ui/navigation-ui.js', 'src/plugins/navigation/navigation.js',
'src/plugins/progress/progress.js', 'src/plugins/navigation-ui/navigation-ui.js',
'src/plugins/rel/rel.js', 'src/plugins/progress/progress.js',
'src/plugins/resize/resize.js', 'src/plugins/rel/rel.js',
'src/plugins/skip/skip.js', 'src/plugins/resize/resize.js',
'src/plugins/stop/stop.js', 'src/plugins/skip/skip.js',
'src/plugins/substep/substep.js', 'src/plugins/stop/stop.js',
'src/plugins/touch/touch.js', 'src/plugins/substep/substep.js',
'src/plugins/toolbar/toolbar.js') 'src/plugins/touch/touch.js',
var output = files.map((f)=>{ 'src/plugins/toolbar/toolbar.js'
return fs.readFileSync(f).toString(); );
}).join('\n') var output = files.map( ( f ) => fs.readFileSync( f ).toString() ).join( '\n' );
var filename = 'js/impress.js'; var filename = 'js/impress.js';
fs.writeFileSync(filename, '// This file was automatically generated from files in src/ directory.\n\n' + output) fs.writeFileSync( filename, '// This file was automatically generated from files in src/ directory.\n\n' + output );
console.log(filename); console.log( filename );
// terser --compress --mangle --comments '/^!/' --source-map --output js/impress.min.js js/impress.js // terser --compress --mangle --comments '/^!/' --source-map --output js/impress.min.js js/impress.js
var code = fs.readFileSync('js/impress.js').toString(); var code = fs.readFileSync( 'js/impress.js' ).toString();
var options = { var options = {
sourceMap: { sourceMap: {
filename: 'js/impress.js', filename: 'js/impress.js',
url: 'js/impress.min.js.map' url: 'js/impress.min.js.map'
}, },
output: { output: {
comments: /^!/ comments: /^!/
} }
}; };
var result = Terser.minify({'js/impress.js': code}, options); var result = Terser.minify( { 'js/impress.js': code }, options );
filename = 'js/impress.min.js'; filename = 'js/impress.min.js';
fs.writeFileSync(filename, result.code); fs.writeFileSync( filename, result.code );
console.log(filename); console.log( filename );
filename = 'js/impress.min.js.map'; filename = 'js/impress.min.js.map';
fs.writeFileSync(filename, result.map); fs.writeFileSync( filename, result.map );
console.log(filename); console.log( filename );
/* Auto generate an index.html that lists all the directories under examples/ // Auto generate an index.html that lists all the directories under examples/
* This is useful for gh-pages, so you can link to http://impress.github.io/impress.js/examples // This is useful for gh-pages, so you can link to http://impress.github.io/impress.js/examples
*/ //
var html_list = '<ul><br />\n' var html_list = '<ul><br />\n';
ls( 'examples/*', { type: 'dir' }).forEach(function(dir) { ls( 'examples/*', { type: 'dir' } ).forEach( function( dir ) {
html_list += ' <li><a href="' + dir['file'] + '/">' + dir['name'] + '</a></li>\n'; html_list += ' <li><a href="' + dir.file + '/">' + dir.name + '</a></li>\n';
}); } );
html_list += '</ul>\n' html_list += '</ul>\n';
var html = '<html>\n<head>\n<title>Example presentations</title>\n</head>\n<body>' var html = '<html>\n<head>\n<title>Example presentations</title>\n</head>\n<body>';
html += '<h1>Example presentations</h1>\n' + html_list html += '<h1>Example presentations</h1>\n' + html_list;
html += '</body>\n</html>' html += '</body>\n</html>';
filename = path.resolve(__dirname, 'examples', 'index.html'); filename = path.resolve( __dirname, 'examples', 'index.html' );
fs.writeFileSync(filename, html); fs.writeFileSync( filename, html );
console.log(filename); console.log( filename );

View File

@@ -58,6 +58,16 @@ window.impress = ( impressConfig ) => {
// So I had it shut up // So I had it shut up
// eslint-disable-next-line prefer-const // eslint-disable-next-line prefer-const
let initializedElements = {}; let initializedElements = {};
const cameraPosition = {
'x': 0,
'y': 0,
'z': 0
};
const cameraRotation = {
'x': 0,
'y': 0,
'z': 0
};
// Check if impress is supported. We use the CSS.supports API which is supported in all // Check if impress is supported. We use the CSS.supports API which is supported in all
// browsers except IE, for which we dropped support with V3 to move forward with state-of-the-art // browsers except IE, for which we dropped support with V3 to move forward with state-of-the-art
@@ -65,7 +75,11 @@ window.impress = ( impressConfig ) => {
// eslint-disable-next-line no-warning-comments // eslint-disable-next-line no-warning-comments
// TODO: Add additional required elements to checks as well // TODO: Add additional required elements to checks as well
const isImpressSupported = ( CSS !== undefined ) && CSS.supports( 'perspective', '100px' ) && ( document.body.classList ) && document.body.dataset; const isImpressSupported = ( CSS !== undefined ) &&
CSS.supports( 'perspective', '100px' ) &&
( document.body.classList ) &&
document.body.dataset;
if ( !isImpressSupported ) { if ( !isImpressSupported ) {
// We can't be sure that classList exists, so let's better not use it // We can't be sure that classList exists, so let's better not use it
document.body.className += ' impress-not-supported'; document.body.className += ' impress-not-supported';
@@ -114,6 +128,7 @@ window.impress = ( impressConfig ) => {
console.log( impressMain.dataset ); console.log( impressMain.dataset );
// If config is passed in via argument, don't use the dataset from the main div, otherwise, parse it // If config is passed in via argument, don't use the dataset from the main div, otherwise, parse it
if ( !impressConfig ) { if ( !impressConfig ) {
// TODO: Initialize
impressConfig = new ImpressConfig(); impressConfig = new ImpressConfig();
} }
@@ -149,6 +164,8 @@ window.impress = ( impressConfig ) => {
rotation.x = rotation.x ?? 0; rotation.x = rotation.x ?? 0;
rotation.y = rotation.y ?? 0; rotation.y = rotation.y ?? 0;
rotation.z = rotation.z ?? 0; rotation.z = rotation.z ?? 0;
// Keep track of all elements
initializedElements[ DOMElementID ] = { initializedElements[ DOMElementID ] = {
coordinates: coordinates, coordinates: coordinates,
rotation: rotation, rotation: rotation,
@@ -226,7 +243,7 @@ window.impress = ( impressConfig ) => {
* @returns {object} Returns an object that contains an object of the coordinates and rotation: * @returns {object} Returns an object that contains an object of the coordinates and rotation:
* { coordinates: { x: number, y: number, z: number }, rotation: { x: number, y: number, z: number } * { coordinates: { x: number, y: number, z: number }, rotation: { x: number, y: number, z: number }
*/ */
const getCurrentPos = () => ( { coordinates: { x: 0, y: 0, z: 0 }, rotation: { x: 0, y: 0, z: 0 } } ); const getCurrentPos = () => ( { coordinates: cameraPosition, rotation: cameraRotation } );
/** /**
@@ -238,12 +255,16 @@ window.impress = ( impressConfig ) => {
/** /**
* Update the impress config. * Update the impress config.
* @param {ImpressConfig} impressConfigs The new impress config * @param {ImpressConfig} impressConfigs The new impress config
* @returns {undefined} Returns nothing * @returns {void} Returns nothing
*/ */
const updateConfig = ( impressConfigs ) => { const updateConfig = ( impressConfigs ) => {
impressConfig = impressConfigs; impressConfig = impressConfigs;
}; };
const tear = () => {
// TODO: Implement
};
// Return all functions that are exposed by impress. This is superior to using classes as we can control what functions we expose. // Return all functions that are exposed by impress. This is superior to using classes as we can control what functions we expose.
return { return {
init, init,
@@ -253,6 +274,7 @@ window.impress = ( impressConfig ) => {
addElement, addElement,
getCurrentPos, getCurrentPos,
getCurrentConfig, getCurrentConfig,
updateConfig updateConfig,
tear
}; };
}; };

View File

@@ -0,0 +1,7 @@
// File name: position.js
// Author: Janis Hutz
// Date created: 2025-10-24 13:54:52
// Date modified: 2025-10-24 13:54:53
// ------

85
src/lib/render.js Normal file
View File

@@ -0,0 +1,85 @@
// File name: render.js
// Author: Janis Hutz
// Date created: 2025-10-24 14:04:37
// Date modified: 2025-10-24 14:26:50
// ------
class ImpressCamera {
constructor () {
this.position = {
'x': 0,
'y': 0,
'z': 0
};
this.rotation = {
'x': 0,
'y': 0,
'z': 0
};
}
setPosition ( x, y, z ) {
this.position.x = x;
this.position.y = y;
this.position.z = z;
}
setRotation ( x, y, z ) {
this.rotation.x = x;
this.rotation.y = y;
this.rotation.z = z;
}
getState () {
return {
'position': this.position,
'rotation': this.rotation
};
}
}
class ImpressElement {
// eslint-disable-next-line max-params
constructor ( element, x, y, z, rotationX, rotationY, rotationZ ) {
this.element = element;
this.position = {
'x': x,
'y': y,
'z': z
};
this.rotation = {
'x': rotationX,
'y': rotationY,
'z': rotationZ
};
}
}
const renderer = () => {
const camera = new ImpressCamera();
const addElement = ( HTMLElement ) => {
// Element will need data-x, data-y, data-z, etc dataset
};
const removeElement = ( HTMLElement ) => {
};
const render = () => {
};
const moveTo = ( x, y, z, rotationX, rotationY, rotationZ ) => {
};
return {
addElement,
removeElement,
moveTo
};
};

View File

@@ -0,0 +1,7 @@
// File name: rotation.js
// Author: Janis Hutz
// Date created: 2025-10-24 13:54:49
// Date modified: 2025-10-24 13:54:50
// ------

View File

@@ -6,7 +6,7 @@
* in modern browsers and inspired by the idea behind prezi.com. * in modern browsers and inspired by the idea behind prezi.com.
* *
* *
* Copyright 2011-2012 Bartek Szopka (@bartaz), 2016-2024 Henrik Ingo (@henrikingo), 2024 Janis Hutz * Copyright 2011-2012 Bartek Szopka (@bartaz), 2016-present Henrik Ingo (@henrikingo), 2025-present Janis Hutz
* and 70+ other contributors * and 70+ other contributors
* *
* Released under the MIT License. * Released under the MIT License.
@@ -63,9 +63,10 @@ window.impressVectorUtil = () => {
* @param {Vector} vec The vector of which to calculate the norm * @param {Vector} vec The vector of which to calculate the norm
* @returns {number} Returns the norm * @returns {number} Returns the norm
*/ */
var norm = function( vec ) { const norm = ( vec ) => Math.sqrt( ( vec.x * vec.x ) + ( vec.y * vec.y ) + ( vec.z * vec.z ) );
return Math.sqrt( ( vec.x * vec.x ) + ( vec.y * vec.y ) + ( vec.z * vec.z ) );
};
const baseChange = ( angle ) => {};
return { return {
norm, norm,

View File

@@ -5,103 +5,109 @@
// Log all QUnit assertions to console.log(), so that they are visible in karma output // Log all QUnit assertions to console.log(), so that they are visible in karma output
QUnit.log( function( details ) { QUnit.log( function( details ) {
console.log( 'QUnit.log: ', details.result, details.message ); console.log( 'QUnit.log: ', details.result, details.message );
} ); } );
var loadIframe = function( src, assert, callback ) { var loadIframe = function( src, assert, callback ) {
console.log( 'Begin loadIframe' ); console.log( 'Begin loadIframe' );
// When running in Karma, the #qunit-fixture appears from somewhere and we can't set its // When running in Karma, the #qunit-fixture appears from somewhere and we can't set its
// contents in advance, so we set it now. // contents in advance, so we set it now.
var fix = document.getElementById( 'qunit-fixture' ); var fix = document.getElementById( 'qunit-fixture' );
fix.innerHTML = [ fix.innerHTML = [
'\n', '\n',
' <iframe id="presentation-iframe"\n', ' <iframe id="presentation-iframe"\n',
' width="595" height="485"\n', ' width="595" height="485"\n',
' frameborder="0" marginwidth="0" marginheight="0" scrolling="no"\n', ' frameborder="0" marginwidth="0" marginheight="0" scrolling="no"\n',
' style="border:1px solid #CCC; max-width: 100%;">\n', ' style="border:1px solid #CCC; max-width: 100%;">\n',
' </iframe>' ' </iframe>'
].join( '' ); ].join( '' );
var iframe = document.getElementById( 'presentation-iframe' ); var iframe = document.getElementById( 'presentation-iframe' );
var onLoad = function() { var onLoad = function() {
assert.ok( true, assert.ok(
'Presentation loaded. iframe.src = ' + iframe.src ); true,
try { 'Presentation loaded. iframe.src = ' + iframe.src
assert.ok( iframe.contentDocument, );
'Verifying that tests can access the presentation inside the iframe. ' + try {
'Note: On Firefox this fails when using paths with "../" parts for the iframe.' ); assert.ok(
} iframe.contentDocument,
catch ( err ) { 'Verifying that tests can access the presentation inside the iframe. ' +
assert.ok( false, 'Note: On Firefox this fails when using paths with "../" parts for the iframe.'
'Error when trying to access presentation in iframe. Note: When using Chrome with ' + );
} catch ( err ) {
assert.ok(
false,
'Error when trying to access presentation in iframe. Note: When using Chrome with ' +
'local files (file:///) this will fail with SecurityError. ' + 'local files (file:///) this will fail with SecurityError. ' +
'You can however use Chrome over Karma.' ); 'You can however use Chrome over Karma.'
} );
console.log( 'End loadIframe' ); }
callback(); console.log( 'End loadIframe' );
}; callback();
};
iframe.addEventListener( 'load', onLoad ); iframe.addEventListener( 'load', onLoad );
assert.ok( iframe.src = src, assert.ok(
'Setting iframe.src = ' + src ); iframe.src = src,
'Setting iframe.src = ' + src
);
}; };
var initPresentation = function( assert, callback, rootId ) { var initPresentation = function( assert, callback, rootId ) {
console.log( 'Begin initPresentation' ); console.log( 'Begin initPresentation' );
var iframe = document.getElementById( 'presentation-iframe' ); var iframe = document.getElementById( 'presentation-iframe' );
var iframeDoc = iframe.contentDocument; var iframeDoc = iframe.contentDocument;
var iframeWin = iframe.contentWindow; var iframeWin = iframe.contentWindow;
// Impress:stepenter is the last event triggered in init(), so we wait for that. // Impress:stepenter is the last event triggered in init(), so we wait for that.
var waitForStepEnter = function( event ) { var waitForStepEnter = function( event ) {
assert.ok( true, 'impress (' + event.target.id + ') is now initialized.' ); assert.ok( true, 'impress (' + event.target.id + ') is now initialized.' );
iframeDoc.removeEventListener( 'impress:stepenter', waitForStepEnterWrapper ); iframeDoc.removeEventListener( 'impress:stepenter', waitForStepEnterWrapper );
console.log( 'End initPresentation' ); console.log( 'End initPresentation' );
callback(); callback();
}; };
// Unfortunately, impress.js uses the impress:stepenter event internally to // Unfortunately, impress.js uses the impress:stepenter event internally to
// do some things related to entering a step. This causes a race condition when // do some things related to entering a step. This causes a race condition when
// we listen for the same event and expect it to be done with everything. // we listen for the same event and expect it to be done with everything.
// We wait 5 ms to resolve the race condition, then it's safe to start testing. // We wait 5 ms to resolve the race condition, then it's safe to start testing.
var waitForStepEnterWrapper = function( event ) { var waitForStepEnterWrapper = function( event ) {
setTimeout( function() { waitForStepEnter( event ); }, 5 ); setTimeout( function() { waitForStepEnter( event ); }, 5 );
}; };
iframeDoc.addEventListener( 'impress:stepenter', waitForStepEnterWrapper ); iframeDoc.addEventListener( 'impress:stepenter', waitForStepEnterWrapper );
assert.strictEqual( iframeWin.impress( rootId ).init(), undefined, 'Initializing impress.' ); assert.strictEqual( iframeWin.impress( rootId ).init(), undefined, 'Initializing impress.' );
}; };
// Helper function to determine whether this browser is supported by // Helper function to determine whether this browser is supported by
// impress.js or not. Copied from impress.js itself. // impress.js or not. Copied from impress.js itself.
var _impressSupported = function() { var _impressSupported = function() {
var pfx = ( function() { var pfx = ( function() {
var style = document.createElement( 'dummy' ).style, var style = document.createElement( 'dummy' ).style,
prefixes = 'Webkit Moz O ms Khtml'.split( ' ' ), prefixes = 'Webkit Moz O ms Khtml'.split( ' ' ),
memory = {}; memory = {};
return function( prop ) { return function( prop ) {
if ( typeof memory[ prop ] === 'undefined' ) { if ( typeof memory[ prop ] === 'undefined' ) {
var ucProp = prop.charAt( 0 ).toUpperCase() + prop.substr( 1 ), var ucProp = prop.charAt( 0 ).toUpperCase() + prop.substr( 1 ),
props = ( prop + ' ' + prefixes.join( ucProp + ' ' ) + ucProp ).split( ' ' ); props = ( prop + ' ' + prefixes.join( ucProp + ' ' ) + ucProp ).split( ' ' );
memory[ prop ] = null; memory[ prop ] = null;
for ( var i in props ) { for ( var i in props ) {
if ( style[ props[ i ] ] !== undefined ) { if ( style[ props[ i ] ] !== undefined ) {
memory[ prop ] = props[ i ]; memory[ prop ] = props[ i ];
break; break;
}
}
} }
} return memory[ prop ];
} };
return memory[ prop ]; } )();
};
} )();
var ua = navigator.userAgent.toLowerCase(); var ua = navigator.userAgent.toLowerCase();
return ( pfx( 'perspective' ) !== null ) && return ( pfx( 'perspective' ) !== null ) &&
( document.body.classList ) && ( document.body.classList ) &&
( document.body.dataset ) && ( document.body.dataset ) &&
( ua.search( /(iphone)|(ipod)|(android)/ ) === -1 ); ( ua.search( /(iphone)|(ipod)|(android)/ ) === -1 );
}; };