22 <div class =" form-group" style =" overflow-x : hidden " >
33 <button
44 v-b-tooltip =" options"
5- @click =" click"
65 :class =" classList"
76 :name =" name"
87 :aria-label =" $attrs['aria-label']"
98 :tabindex =" $attrs['tabindex']"
109 :disabled =" showSpinner"
10+ @click =" click"
1111 >
1212 <b-spinner v-if =" showSpinner" small ></b-spinner >
1313 {{ showSpinner ? (!loadingLabel ? "Loading..." : loadingLabel) : label }}
1414 </button >
1515 </div >
1616</template >
1717
18+ <!-- eslint-disable import/no-extraneous-dependencies -->
19+ <!-- eslint-disable import/no-unresolved -->
20+ <!-- eslint-disable import/extensions -->
1821<script >
19- import Mustache from ' mustache' ;
20- import { mapActions , mapState } from " vuex" ;
21- import { getValidPath } from ' @/mixins' ;
22+ import Mustache from " mustache" ;
23+ import { mapState } from " vuex" ;
24+ import { stringify } from " flatted" ;
25+ import { getValidPath } from " @/mixins" ;
2226import Worker from " @/workers/worker.js?worker&inline" ;
2327import { findRootScreen } from " @/mixins/DataReference" ;
24- import { stringify } from ' flatted' ;
2528
2629export default {
2730 mixins: [getValidPath],
@@ -37,7 +40,8 @@ export default {
3740 " transientData" ,
3841 " loading" ,
3942 " loadingLabel" ,
40- " handler"
43+ " handler" ,
44+ " handlerSecurityEnabled"
4145 ],
4246 data () {
4347 return {
@@ -47,30 +51,35 @@ export default {
4751 computed: {
4852 ... mapState (" globalErrorsModule" , [" valid" ]),
4953 classList () {
50- let variant = this .variant || ' primary' ;
54+ const variant = this .variant || " primary" ;
5155 return {
5256 btn: true ,
53- [' btn-' + variant]: true ,
54- disabled: this .event === ' submit' && ! this .valid
57+ [` btn-${ variant} ` ]: true ,
58+ disabled: this .event === " submit" && ! this .valid
5559 };
5660 },
5761 options () {
58- if (! this .tooltip || this .event === ' submit' ) {
62+ if (! this .tooltip || this .event === " submit" ) {
5963 return {};
6064 }
6165
62- let content = ' ' ;
66+ let content = " " ;
6367 try {
64- content = Mustache .render (this .tooltip .content || ' ' , this .transientData );
65- } catch (error) { error; }
68+ content = Mustache .render (
69+ this .tooltip .content || " " ,
70+ this .transientData
71+ );
72+ } catch (error) {
73+ console .error (error);
74+ }
6675
6776 return {
6877 title: content,
6978 html: true ,
70- placement: this .tooltip .position || ' ' ,
71- trigger: ' hover' ,
72- variant: this .tooltip .variant || ' ' ,
73- boundary: ' window' ,
79+ placement: this .tooltip .position || " " ,
80+ trigger: " hover" ,
81+ variant: this .tooltip .variant || " " ,
82+ boundary: " window"
7483 };
7584 },
7685 buttonInfo () {
@@ -92,74 +101,150 @@ export default {
92101 }
93102 },
94103 async click () {
95- if (this .event === ' script' ) {
96- const trueValue = this .fieldValue || ' 1' ;
97- const value = (this .value == trueValue) ? null : trueValue;
98- this .$emit (' input' , value);
104+ if (this .event === " script" ) {
105+ const trueValue = this .fieldValue || " 1" ;
106+ // eslint-disable-next-line eqeqeq
107+ const value = this .value == trueValue ? null : trueValue;
108+ this .$emit (" input" , value);
99109 // Run handler after setting the value
100110 await this .runHandler ();
101111 }
102- if (this .event !== ' pageNavigate' && this .name ) {
112+ if (this .event !== " pageNavigate" && this .name ) {
103113 this .setValue (this .$parent , this .name , this .fieldValue );
104114 }
105- if (this .event === ' submit' ) {
115+ if (this .event === " submit" ) {
106116 if (this .loading && this .valid ) {
107117 this .showSpinner = true ;
108118 }
109- this .$emit (' input' , this .fieldValue );
119+ this .$emit (" input" , this .fieldValue );
110120 // Run handler after setting the value
111121 await this .runHandler ();
112122 this .$nextTick (() => {
113- this .$emit (' submit' , this .eventData , this .loading , this .buttonInfo );
123+ this .$emit (" submit" , this .eventData , this .loading , this .buttonInfo );
114124 });
115125 return ;
116126 }
117- if (this .event === ' pageNavigate' ) {
127+ if (this .event === " pageNavigate" ) {
118128 // Run handler for page navigate
119129 await this .runHandler ();
120130 }
121131 this .$emit (this .event , this .eventData );
122- if (this .event === ' pageNavigate' ) {
123- this .$emit (' page-navigate' , this .eventData );
132+ if (this .event === " pageNavigate" ) {
133+ this .$emit (" page-navigate" , this .eventData );
124134 }
125135 },
126136 runHandler () {
127- if (this .handler ) {
128- return new Promise ((resolve , reject ) => {
129- try {
130- const rootScreen = findRootScreen (this );
131- const data = rootScreen .vdata ;
132- const scope = this .transientData ;
137+ if (! this .handler ) {
138+ return Promise .resolve ();
139+ }
133140
134- const worker = new Worker ();
135- // Send the handler code to the worker
136- worker .postMessage ({
137- fn: this .handler ,
138- dataRefs: stringify ({data, scope}),
139- });
141+ const rootScreen = findRootScreen (this );
142+ const data = rootScreen .vdata ;
143+ const scope = this .transientData ;
140144
141- // Listen for the result from the worker
142- worker .onmessage = (e ) => {
143- if (e .data .error ) {
144- reject (e .data .error );
145- } else if (e .data .result ) {
146- // Update the data with the result
147- Object .keys (e .data .result ).forEach (key => {
148- if (key === ' _root' ) {
149- Object .assign (data, e .data .result [key]);
150- } else {
151- scope[key] = e .data .result [key];
152- }
153- });
154- resolve ();
155- }
156- };
157- } catch (error) {
158- console .error (" ❌ There is an error in the button handler" , error);
159- }
160- });
145+ if (this .handlerSecurityEnabled === false ) {
146+ return this .executeHandlerWithoutWorker (data, scope);
161147 }
148+
149+ return this .executeHandlerWithWorker (data, scope);
150+ },
151+ executeHandlerWithWorker (data , scope ) {
152+ return new Promise ((resolve , reject ) => {
153+ try {
154+ const worker = new Worker ();
155+ worker .postMessage ({
156+ fn: this .handler ,
157+ dataRefs: stringify ({ data, scope })
158+ });
159+
160+ worker .onmessage = (e ) => {
161+ worker .terminate ();
162+ if (e .data .error ) {
163+ console .error (
164+ " There is an error in the button handler" ,
165+ e .data .error
166+ );
167+ reject (e .data .error );
168+ return ;
169+ }
170+ this .applyHandlerResult (e .data .result , data, scope);
171+ resolve ();
172+ };
173+
174+ worker .onerror = (errorEvent ) => {
175+ worker .terminate ();
176+ console .error (
177+ " There is an error in the button handler" ,
178+ errorEvent
179+ );
180+ reject (errorEvent);
181+ };
182+ } catch (error) {
183+ console .error (" There is an error in the button handler" , error);
184+ reject (error);
185+ }
186+ });
187+ },
188+ executeHandlerWithoutWorker (data , scope ) {
189+ const hasDataReferenceHelper =
190+ typeof this .getScreenDataReference === " function" ;
191+ const dataReference = hasDataReferenceHelper
192+ ? this .getScreenDataReference (null , (screen , name , value ) => {
193+ screen .$set (screen .vdata , name, value);
194+ })
195+ : data;
196+ const parentReference =
197+ hasDataReferenceHelper && dataReference
198+ ? dataReference ._parent
199+ : undefined ;
200+ const context = scope || dataReference;
201+ const toRaw = (item ) => (item && item[Symbol .for (" __v_raw" )]) || item;
202+ const functionBody = ` return (async () => { ${ this .handler } })();` ;
203+
204+ try {
205+ // eslint-disable-next-line no-new-func, max-len
206+ const userFunc = new Function (" data" , " parent" , " toRaw" , functionBody); // NOSONAR. This dynamic code execution is safe because it only occurs when the user has explicitly disabled the security worker.
207+ const result = userFunc .apply (context, [
208+ dataReference,
209+ parentReference,
210+ toRaw
211+ ]);
212+ return this .resolveHandlerResult (result, data, scope);
213+ } catch (error) {
214+ console .error (" There is an error in the button handler" , error);
215+ return Promise .reject (error);
216+ }
217+ },
218+ resolveHandlerResult (result , data , scope ) {
219+ if (result && typeof result .then === " function" ) {
220+ return result
221+ .then ((resolved ) => {
222+ this .applyHandlerResult (resolved, data, scope);
223+ })
224+ .catch ((error ) => {
225+ console .error (" There is an error in the button handler" , error);
226+ throw error;
227+ });
228+ }
229+
230+ this .applyHandlerResult (result, data, scope);
231+ return Promise .resolve ();
232+ },
233+ applyHandlerResult (result , data , scope ) {
234+ if (! result || typeof result !== " object" ) {
235+ return ;
236+ }
237+
238+ const targetScope = scope || this .transientData || {};
239+
240+ Object .keys (result).forEach ((key ) => {
241+ if (key === " _root" ) {
242+ Object .assign (data, result[key]);
243+ } else {
244+ this .$set (targetScope, key, result[key]);
245+ }
246+ });
162247 }
163- },
248+ }
164249};
165250 </script >
0 commit comments