@@ -48,6 +48,35 @@ class p5 {
4848 static _friendlyFileLoadError = ( ) => { } ;
4949
5050 constructor ( sketch , node ) {
51+ // Apply addon defined decorations
52+ if ( p5 . decorations . size > 0 ) {
53+ for ( const [ patternArray , decoration ] of p5 . decorations ) {
54+ for ( const member in p5 . prototype ) {
55+ // Member must be a function
56+ if ( typeof p5 . prototype [ member ] !== 'function' ) continue ;
57+
58+ if ( ! patternArray . some ( pattern => {
59+ if ( typeof pattern === 'string' ) {
60+ return pattern === member ;
61+ } else if ( pattern instanceof RegExp ) {
62+ return pattern . test ( member ) ;
63+ }
64+ } ) ) continue ;
65+
66+ p5 . prototype [ member ] = decoration ( p5 . prototype [ member ] , {
67+ kind : 'method' ,
68+ name : member ,
69+ access : { } ,
70+ static : false ,
71+ private : false ,
72+ addInitializer ( initializer ) { }
73+ } ) ;
74+ }
75+ }
76+
77+ p5 . decorations . clear ( ) ;
78+ }
79+
5180 //////////////////////////////////////////////
5281 // PRIVATE p5 PROPERTIES AND METHODS
5382 //////////////////////////////////////////////
@@ -77,122 +106,7 @@ class p5 {
77106 // ensure correct reporting of window dimensions
78107 this . _updateWindowSize ( ) ;
79108
80- const bindGlobal = property => {
81- if ( property === 'constructor' ) return ;
82-
83- // Common setter for all property types
84- const createSetter = ( ) => newValue => {
85- Object . defineProperty ( window , property , {
86- configurable : true ,
87- enumerable : true ,
88- value : newValue ,
89- writable : true
90- } ) ;
91- if ( ! p5 . disableFriendlyErrors ) {
92- console . log ( `You just changed the value of "${ property } ", which was a p5 global value. This could cause problems later if you're not careful.` ) ;
93- }
94- } ;
95-
96- // Check if this property has a getter on the instance or prototype
97- const instanceDescriptor = Object . getOwnPropertyDescriptor ( this , property ) ;
98- const prototypeDescriptor = Object . getOwnPropertyDescriptor ( p5 . prototype , property ) ;
99- const hasGetter = ( instanceDescriptor && instanceDescriptor . get ) ||
100- ( prototypeDescriptor && prototypeDescriptor . get ) ;
101-
102- // Only check if it's a function if it doesn't have a getter
103- // to avoid actually evaluating getters before things like the
104- // renderer are fully constructed
105- let isPrototypeFunction = false ;
106- let isConstant = false ;
107- let constantValue ;
108-
109- if ( ! hasGetter ) {
110- const prototypeValue = p5 . prototype [ property ] ;
111- isPrototypeFunction = typeof prototypeValue === 'function' ;
112-
113- // Check if this is a true constant from the constants module
114- if ( ! isPrototypeFunction && constants [ property ] !== undefined ) {
115- isConstant = true ;
116- constantValue = prototypeValue ;
117- }
118- }
119-
120- if ( isPrototypeFunction ) {
121- // For regular functions, cache the bound function
122- const boundFunction = p5 . prototype [ property ] . bind ( this ) ;
123- if ( p5 . disableFriendlyErrors ) {
124- Object . defineProperty ( window , property , {
125- configurable : true ,
126- enumerable : true ,
127- value : boundFunction ,
128- } ) ;
129- } else {
130- Object . defineProperty ( window , property , {
131- configurable : true ,
132- enumerable : true ,
133- get ( ) {
134- return boundFunction ;
135- } ,
136- set : createSetter ( )
137- } ) ;
138- }
139- } else if ( isConstant ) {
140- // For constants, cache the value directly
141- if ( p5 . disableFriendlyErrors ) {
142- Object . defineProperty ( window , property , {
143- configurable : true ,
144- enumerable : true ,
145- value : constantValue ,
146- } ) ;
147- } else {
148- Object . defineProperty ( window , property , {
149- configurable : true ,
150- enumerable : true ,
151- get ( ) {
152- return constantValue ;
153- } ,
154- set : createSetter ( )
155- } ) ;
156- }
157- } else if ( hasGetter || ! isPrototypeFunction ) {
158- // For properties with getters or non-function properties, use lazy optimization
159- // On first access, determine the type and optimize subsequent accesses
160- let lastFunction = null ;
161- let boundFunction = null ;
162- let isFunction = null ; // null = unknown, true = function, false = not function
163-
164- Object . defineProperty ( window , property , {
165- configurable : true ,
166- enumerable : true ,
167- get : ( ) => {
168- const currentValue = this [ property ] ;
169-
170- if ( isFunction === null ) {
171- // First access - determine type and optimize
172- isFunction = typeof currentValue === 'function' ;
173- if ( isFunction ) {
174- lastFunction = currentValue ;
175- boundFunction = currentValue . bind ( this ) ;
176- return boundFunction ;
177- } else {
178- return currentValue ;
179- }
180- } else if ( isFunction ) {
181- // Optimized function path - only rebind if function changed
182- if ( currentValue !== lastFunction ) {
183- lastFunction = currentValue ;
184- boundFunction = currentValue . bind ( this ) ;
185- }
186- return boundFunction ;
187- } else {
188- // Optimized non-function path
189- return currentValue ;
190- }
191- } ,
192- set : createSetter ( )
193- } ) ;
194- }
195- } ;
109+ const bindGlobal = createBindGlobal ( this ) ;
196110 // If the user has created a global setup or draw function,
197111 // assume "global" mode and make everything global (i.e. on the window)
198112 if ( ! sketch ) {
@@ -259,6 +173,7 @@ class p5 {
259173
260174 static registerAddon ( addon ) {
261175 const lifecycles = { } ;
176+
262177 addon ( p5 , p5 . prototype , lifecycles ) ;
263178
264179 const validLifecycles = Object . keys ( p5 . lifecycleHooks ) ;
@@ -269,6 +184,13 @@ class p5 {
269184 }
270185 }
271186
187+ static decorations = new Map ( ) ;
188+ static decorateHelper ( pattern , decoration ) {
189+ let patternArray = pattern ;
190+ if ( ! Array . isArray ( pattern ) ) patternArray = [ pattern ] ;
191+ p5 . decorations . set ( patternArray , decoration ) ;
192+ }
193+
272194 #customActions = { } ;
273195 _customActions = new Proxy ( { } , {
274196 get : ( target , prop ) => {
@@ -511,6 +433,96 @@ class p5 {
511433 }
512434}
513435
436+ // Global helper function for binding properties to window in global mode
437+ function createBindGlobal ( instance ) {
438+ return function bindGlobal ( property ) {
439+ if ( property === 'constructor' ) return ;
440+
441+ // Check if this property has a getter on the instance or prototype
442+ const instanceDescriptor = Object . getOwnPropertyDescriptor (
443+ instance ,
444+ property
445+ ) ;
446+ const prototypeDescriptor = Object . getOwnPropertyDescriptor (
447+ p5 . prototype ,
448+ property
449+ ) ;
450+ const hasGetter = ( instanceDescriptor && instanceDescriptor . get ) ||
451+ ( prototypeDescriptor && prototypeDescriptor . get ) ;
452+
453+ // Only check if it's a function if it doesn't have a getter
454+ // to avoid actually evaluating getters before things like the
455+ // renderer are fully constructed
456+ let isPrototypeFunction = false ;
457+ let isConstant = false ;
458+ let constantValue ;
459+
460+ if ( ! hasGetter ) {
461+ const prototypeValue = p5 . prototype [ property ] ;
462+ isPrototypeFunction = typeof prototypeValue === 'function' ;
463+
464+ // Check if this is a true constant from the constants module
465+ if ( ! isPrototypeFunction && constants [ property ] !== undefined ) {
466+ isConstant = true ;
467+ constantValue = prototypeValue ;
468+ }
469+ }
470+
471+ if ( isPrototypeFunction ) {
472+ // For regular functions, cache the bound function
473+ const boundFunction = p5 . prototype [ property ] . bind ( instance ) ;
474+ Object . defineProperty ( window , property , {
475+ configurable : true ,
476+ enumerable : true ,
477+ value : boundFunction
478+ } ) ;
479+ } else if ( isConstant ) {
480+ // For constants, cache the value directly
481+ Object . defineProperty ( window , property , {
482+ configurable : true ,
483+ enumerable : true ,
484+ value : constantValue
485+ } ) ;
486+ } else if ( hasGetter || ! isPrototypeFunction ) {
487+ // For properties with getters or non-function properties, use lazy optimization
488+ // On first access, determine the type and optimize subsequent accesses
489+ let lastFunction = null ;
490+ let boundFunction = null ;
491+ let isFunction = null ; // null = unknown, true = function, false = not function
492+
493+ Object . defineProperty ( window , property , {
494+ configurable : true ,
495+ enumerable : true ,
496+ get : ( ) => {
497+ const currentValue = instance [ property ] ;
498+
499+ if ( isFunction === null ) {
500+ // First access - determine type and optimize
501+ isFunction = typeof currentValue === 'function' ;
502+ if ( isFunction ) {
503+ lastFunction = currentValue ;
504+ boundFunction = currentValue . bind ( instance ) ;
505+ return boundFunction ;
506+ } else {
507+ return currentValue ;
508+ }
509+ } else if ( isFunction ) {
510+ // Optimized function path - only rebind if function changed
511+ if ( currentValue !== lastFunction ) {
512+ lastFunction = currentValue ;
513+ boundFunction = currentValue . bind ( instance ) ;
514+ }
515+ return boundFunction ;
516+ } else {
517+ // Optimized non-function path
518+ return currentValue ;
519+ }
520+ }
521+ } ) ;
522+ }
523+ } ;
524+ }
525+
514526// Attach constants to p5 prototype
515527for ( const k in constants ) {
516528 p5 . prototype [ k ] = constants [ k ] ;
@@ -745,8 +757,6 @@ for (const k in constants) {
745757 * </code>
746758 * </div>
747759 */
748- p5 . disableFriendlyErrors = false ;
749-
750760import transform from './transform' ;
751761import structure from './structure' ;
752762import environment from './environment' ;
0 commit comments