@@ -48,6 +48,35 @@ class p5 {
48
48
static _friendlyFileLoadError = ( ) => { } ;
49
49
50
50
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
+
51
80
//////////////////////////////////////////////
52
81
// PRIVATE p5 PROPERTIES AND METHODS
53
82
//////////////////////////////////////////////
@@ -77,122 +106,7 @@ class p5 {
77
106
// ensure correct reporting of window dimensions
78
107
this . _updateWindowSize ( ) ;
79
108
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 ) ;
196
110
// If the user has created a global setup or draw function,
197
111
// assume "global" mode and make everything global (i.e. on the window)
198
112
if ( ! sketch ) {
@@ -259,6 +173,7 @@ class p5 {
259
173
260
174
static registerAddon ( addon ) {
261
175
const lifecycles = { } ;
176
+
262
177
addon ( p5 , p5 . prototype , lifecycles ) ;
263
178
264
179
const validLifecycles = Object . keys ( p5 . lifecycleHooks ) ;
@@ -269,6 +184,13 @@ class p5 {
269
184
}
270
185
}
271
186
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
+
272
194
#customActions = { } ;
273
195
_customActions = new Proxy ( { } , {
274
196
get : ( target , prop ) => {
@@ -511,6 +433,96 @@ class p5 {
511
433
}
512
434
}
513
435
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
+
514
526
// Attach constants to p5 prototype
515
527
for ( const k in constants ) {
516
528
p5 . prototype [ k ] = constants [ k ] ;
@@ -745,8 +757,6 @@ for (const k in constants) {
745
757
* </code>
746
758
* </div>
747
759
*/
748
- p5 . disableFriendlyErrors = false ;
749
-
750
760
import transform from './transform' ;
751
761
import structure from './structure' ;
752
762
import environment from './environment' ;
0 commit comments