p5
Version:
[](https://www.npmjs.com/package/p5)
455 lines (427 loc) • 14.5 kB
JavaScript
import { p as p5 } from '../../main-BKN5yFoS.js';
import { translator } from '../internationalization.js';
import { v as constants } from '../../constants-BRcElHU3.js';
import '../transform.js';
import '../structure.js';
import '../environment.js';
import '../../math/p5.Vector.js';
import '../../rendering-CvUVN-Vb.js';
import '../../creating_reading-Cr8L2Jnm.js';
import 'colorjs.io/fn';
import '../../color/color_spaces/hsb.js';
import '../../dom/p5.Element.js';
import '../../dom/p5.File.js';
import '../../io/p5.XML.js';
import '../../p5.Renderer-R23xoC7s.js';
import '../../image/filters.js';
import '../../shape/custom_shapes.js';
import '../States.js';
import '../../io/utilities.js';
import 'file-saver';
import '../../dom/p5.MediaElement.js';
import '../../shape/2d_primitives.js';
import '../helpers.js';
import '../../shape/attributes.js';
import '../../shape/curves.js';
import '../../shape/vertex.js';
import '../../color/setting.js';
import 'omggif';
import '../../io/csv.js';
import 'gifenc';
import '../../image/pixels.js';
import '../../webgl/GeometryBuilder.js';
import '../../math/p5.Matrix.js';
import '../../math/Matrices/Matrix.js';
import '../../math/Matrices/MatrixInterface.js';
import '../../webgl/p5.Geometry.js';
import '../../webgl/p5.DataArray.js';
import '../../webgl/p5.Quat.js';
import '../../webgl/p5.RenderBuffer.js';
import '../../webgl/ShapeBuilder.js';
import 'libtess';
import '../../webgl/GeometryBufferCache.js';
import '../../image/const.js';
import '../../math/trigonometry.js';
import '../../image/filterRenderer2D.js';
import 'i18next';
import 'i18next-browser-languagedetector';
/**
* @for p5
* @requires core
*/
/**
* Checks if any p5.js constant/function is declared outside of setup()
* and draw() function. Also checks any reserved constant/function is
* redeclared.
*
* Generates and prints a friendly error message using key:
* "fes.sketchReaderErrors.reservedConst",
* "fes.sketchReaderErrors.reservedFunc".
*
* @method _fesCodeReader
* @private
*/
if (typeof IS_MINIFIED !== 'undefined') {
p5._fesCodeReader = () => {};
} else {
//list of functions to ignore as they either
//are ment to be defined or generate false positive
//outputs
const ignoreFunction = [
'setup',
'draw',
'preload',
'deviceMoved',
'deviceTurned',
'deviceShaken',
'doubleClicked',
'mousePressed',
'mouseReleased',
'mouseMoved',
'mouseDragged',
'mouseClicked',
'mouseWheel',
'touchStarted',
'touchMoved',
'touchEnded',
'keyPressed',
'keyReleased',
'keyTyped',
'windowResized',
'name',
'parent',
'toString',
'print',
'stop',
'onended'
];
/**
* Takes a list of variables defined by the user in the code
* as an array and checks if the list contains p5.js constants and functions.
*
* @method checkForConstsAndFuncs
* @private
* @param {Array} variableArray
*/
const checkForConstsAndFuncs = variableArray => {
for (let i = 0; i < variableArray.length; i++) {
//if the element in variableArray is a p5.js constant then the below condidion
//will be true, hence a match is found
if (constants[variableArray[i]] !== undefined) {
let url = `https://p5js.org/reference/p5/${variableArray[i]}`;
//display the FES message if a match is found
p5._friendlyError(
translator('fes.sketchReaderErrors.reservedConst', {
url,
symbol: variableArray[i]
})
);
return;
//if match found then end search
}
}
let p5Constructors = {};
for (let key of Object.keys(p5)) {
// Get a list of all constructors in p5. They are functions whose names
// start with a capital letter
if (typeof p5[key] === 'function' && key[0] !== key[0].toLowerCase()) {
p5Constructors[key] = p5[key];
}
}
for (let i = 0; i < variableArray.length; i++) {
//ignoreFunction contains the list of functions to be ignored
if (!ignoreFunction.includes(variableArray[i])) {
const keyArray = Object.keys(p5Constructors);
let j = 0;
//for every function name obtained check if it matches any p5.js function name
for (; j < keyArray.length; j++) {
if (
p5Constructors[keyArray[j]].prototype[variableArray[i]] !==
undefined
) {
//if a p5.js function is used ie it is in the funcs array
let url = `https://p5js.org/reference/p5/${variableArray[i]}`;
p5._friendlyError(
translator('fes.sketchReaderErrors.reservedFunc', {
url,
symbol: variableArray[i]
})
);
return;
}
}
}
}
};
//these regex are used to perform variable extraction
//visit https://regexr.com/ for the detailed view
const optionalVarKeyword = /(?:(?:let|const|var)\s+)?/;
// Bracketed expressions start with an opening bracket, some amount of non
// bracket characters, then a closing bracket. Note that this won't properly
// parse nested brackets: `constrain(millis(), 0, 1000)` will match
// `constrain(millis()` only, but will still fail gracefully and not try to
// mistakenly read any subsequent code as assignment expressions.
const roundBracketedExpr = /(?:\([^)]*\))/;
const squareBracketedExpr = /(?:\[[^\]]*\])/;
const curlyBracketedExpr = /(?:\{[^}]*\})/;
const bracketedExpr = new RegExp(
[roundBracketedExpr, squareBracketedExpr, curlyBracketedExpr]
.map(regex => regex.source)
.join('|')
);
// In an a = b expression, `b` can be any character up to a newline or comma,
// unless the comma is inside of a bracketed expression of some kind (to make
// sure we parse function calls with multiple arguments properly.)
const rightHandSide = new RegExp('(?:' + bracketedExpr.source + '|[^\\n,])+');
const leftHandSide = /([\w$]+)/;
const assignmentOperator = /\s*=\s*/;
const singleAssignment = new RegExp(
leftHandSide.source + assignmentOperator.source + rightHandSide.source
);
const listSeparator = /,\s*/;
const oneOrMoreAssignments = new RegExp(
'(?:' +
singleAssignment.source +
listSeparator.source +
')*' +
singleAssignment.source
);
const assignmentStatement = new RegExp(
'^' + optionalVarKeyword.source + oneOrMoreAssignments.source
);
const letConstName = /(?:(?:let|const)\s+)([\w$]+)/;
/**
* Takes an array in which each element is a line of code
* containing a variable definition(Eg: arr=['let x = 100', 'const y = 200'])
* and extracts the variables defined.
*
* @method extractVariables
* @private
* @param {Array} linesArray array of lines of code
*/
const extractVariables = linesArray => {
//extract variable names from the user's code
let matches = [];
linesArray.forEach(ele => {
// Match 0 is the part of the line of code that the regex looked at.
// Matches 1 and onward will be only the variable names on the left hand
// side of assignment expressions.
const match = ele.match(assignmentStatement);
if (!match) return;
matches.push(...match.slice(1).filter(group => group !== undefined));
});
//check if the obtained variables are a part of p5.js or not
checkForConstsAndFuncs(matches);
};
/**
* Takes an array in which each element is a line of code
* containing a function definition(array=['let x = () => {...}'])
* and extracts the functions defined.
*
* @method extractFuncVariables
* @private
* @param {Array} linesArray array of lines of code
*/
const extractFuncVariables = linesArray => {
let matches = [];
//RegExp to extract function names from let/const x = function()...
//visit https://regexr.com/ for the detailed view.
linesArray.forEach(ele => {
let m = ele.match(letConstName);
if (m !== null) matches.push(ele.match(letConstName)[1]);
});
//matches array contains the names of the functions
checkForConstsAndFuncs(matches);
};
/**
* Converts code written by the user to an array
* every element of which is a seperate line of code.
*
* @method codeToLines
* @private
* @param {String} code code written by the user
*/
const codeToLines = code => {
//convert code to array of code and filter out
//unnecessary lines
let arrayVariables = code
.split('\n')
.map(line => line.trim())
.filter(
line =>
line !== '' &&
!line.includes('//') &&
(line.includes('let') || line.includes('const')) &&
(!line.includes('=>') && !line.includes('function'))
//filter out lines containing variable names
);
//filter out lines containing function names
let arrayFunctions = code
.split('\n')
.map(line => line.trim())
.filter(
line =>
line !== '' &&
!line.includes('//') &&
(line.includes('let') || line.includes('const')) &&
(line.includes('=>') || line.includes('function'))
);
//pass the relevant array to a function which will extract all the variables/functions names
extractVariables(arrayVariables);
extractFuncVariables(arrayFunctions);
};
/**
* Remove multiline comments and the content inside it.
*
* @method removeMultilineComments
* @private
* @param {String} code code written by the user
* @returns {String}
*/
const removeMultilineComments = code => {
let start = code.indexOf('/*');
let end = code.indexOf('*/');
//create a new string which don't have multiline comments
while (start !== -1 && end !== -1) {
if (start === 0) {
code = code.slice(end + 2);
} else code = code.slice(0, start) + code.slice(end + 2);
start = code.indexOf('/*');
end = code.indexOf('*/');
}
return code;
};
/**
* Checks if any p5.js constant or function is declared outside a function
* and reports it if found.
*
* @method globalConstFuncCheck
* @private
* @returns {Boolean}
*/
const globalConstFuncCheck = () => {
// generate all the const key data as an array
const tempArray = Object.keys(constants);
let element;
for (let i = 0; i < tempArray.length; i++) {
try {
//if the user has not declared p5.js constant anywhere outside the
//setup or draw function then this will throw an
//error.
element = eval(tempArray[i]);
} catch (e) {
//We are catching the error due to the above mentioned
//reason. Since there is no declaration of constant everything
//is OK so we will skip the current iteration and check for the
//next element.
continue;
}
//if we are not getting an error this means
//user have changed the value. We will check
//if the value is changed and if it is changed
//then report.
if (constants[tempArray[i]] !== element) {
let url = `https://p5js.org/reference/p5/${tempArray[i]}`;
p5._friendlyError(
translator('fes.sketchReaderErrors.reservedConst', {
url,
symbol: tempArray[i]
})
);
//if a p5.js constant is already reported then no need to check
//for p5.js functions.
return true;
}
}
//the below code gets a list of p5.js functions
let p5Constructors = {};
for (let key of Object.keys(p5)) {
// Get a list of all constructors in p5. They are functions whose names
// start with a capital letter
if (typeof p5[key] === 'function' && key[0] !== key[0].toLowerCase()) {
p5Constructors[key] = p5[key];
}
}
const keyArray = Object.keys(p5Constructors);
const classesWithGlobalFns = ['Renderer', 'Renderer2D', 'RendererGL'];
let functionArray = [];
//get the names of all p5.js functions which are available globally
for (let i = 0; i < classesWithGlobalFns.length; i++) {
functionArray.push(...Object.keys(
p5Constructors[classesWithGlobalFns[i]].prototype
));
}
//we have p5.js function names with us so we will check
//if they have been declared or not.
for (let i = 0; i < functionArray.length; i++) {
//ignoreFunction contains the list of functions to be ignored
if (!ignoreFunction.includes(functionArray[i])) {
try {
//if we get an error that means the function is not declared
element = eval(functionArray[i]);
} catch (e) {
//we will skip the iteration
continue;
}
//if we are not getting an error this means
//user have used p5.js function. Check if it is
//changed and if so then report it.
for (let k = 0; k < keyArray.length; k++) {
if (
p5Constructors[keyArray[k]].prototype[functionArray[i]] ===
undefined
);
else {
if (
p5Constructors[keyArray[k]].prototype[functionArray[i]] !==
element
) {
let url = `https://p5js.org/reference/p5/${functionArray[i]}`;
p5._friendlyError(
translator('fes.sketchReaderErrors.reservedFunc', {
url,
symbol: functionArray[i]
})
);
return true;
}
}
}
}
}
};
/**
* Initiates the sketch_reader's processes.
* Obtains the code in setup and draw function
* and forwards it for further processing and evaluation.
*
* @method fesCodeReader
* @private
*/
const fesCodeReader = () => {
//moveAhead will determine if a match is found outside
//the setup and draw function. If a match is found then
//to prevent further potential reporting we will exit immidiately
let moveAhead = globalConstFuncCheck();
if (moveAhead) return;
let code = '';
try {
//get code from setup
code += '' + setup;
} catch (e) {
code += '';
}
try {
//get code from draw
code += '\n' + draw;
} catch (e) {
code += '';
}
if (code === '') return;
code = removeMultilineComments(code);
codeToLines(code);
};
p5._fesCodeReader = fesCodeReader;
window.addEventListener('p5Ready', p5._fesCodeReader);
}
export { p5 as default };