yelda-webchat 1.1.16 → 1.1.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yelda-webchat",
3
- "version": "1.1.16",
3
+ "version": "1.1.18",
4
4
  "description": "Website Chat Plugin",
5
5
  "main": "./dist/js/yeldaWebchatInjector.min.js",
6
6
  "license": "MIT",
@@ -29,7 +29,8 @@ module.exports = {
29
29
  LISTEN_URL_UPDATE: 'listenUrlUpdate',
30
30
  ABSTAIN_URL_UPDATE: 'abstainUrlUpdate',
31
31
  ADD_BUBBLE_TEXT: 'addBubbleText',
32
- ADD_MINIMAL_NOTIFICATION_TEXT: 'addMinimalNotificationText'
32
+ ADD_MINIMAL_NOTIFICATION_TEXT: 'addMinimalNotificationText',
33
+ EXEC_JS_FUNC: 'execJsFunc'
33
34
  }
34
35
  },
35
36
  CHAT_BUBBLE_EXTERNAL_ENDPOINT: 'https://webchat.yelda.ai/webchat',
@@ -8,7 +8,117 @@ import 'lg-share.js'
8
8
 
9
9
  import config from '../config'
10
10
 
11
+ /**
12
+ * Check whether the string is url encoded
13
+ * @param {String} str
14
+ * @returns {Boolean}
15
+ */
16
+ const isStrLikelyEncoded = (str) => {
17
+ // Check if the string contains any characters that would typically need encoding
18
+ const needsEncoding = /[?&=<>#%{}|\\\^~\[\]`;"\s]/.test(str)
19
+
20
+ // Check if the string contains any non-ASCII characters
21
+ const hasNonASCII = /[^\x00-\x7F]/.test(str);
22
+
23
+ // Check if the string contains any percent-encoded characters
24
+ const hasPercentEncoding = /%[0-9A-Fa-f]{2}/.test(str)
25
+
26
+ // Return true if any of the conditions are met
27
+ return needsEncoding || hasNonASCII || hasPercentEncoding
28
+ }
29
+
30
+ /**
31
+ * Execute the function by name with the arguments
32
+ * @param {String} functionName
33
+ * @param {Context} context
34
+ * @param {Array} args
35
+ * @return {Any|null}
36
+ */
37
+ const executeFunctionByName = (functionName, context, args) => {
38
+ try {
39
+ const namespaces = functionName.split(".")
40
+ const func = namespaces.pop()
41
+ for(const element of namespaces) {
42
+ context = context[element]
43
+ }
44
+
45
+ return context[func].apply(context, args)
46
+ } catch(error) {
47
+ console.error('executeFunctionByName error', error)
48
+ return null
49
+ }
50
+ }
51
+
52
+
11
53
  class YeldaChat {
54
+ /**
55
+ * Parse the function string to get the function name & arguments
56
+ * @param {String} str
57
+ * @returns {Object|null} {functionName, args}
58
+ */
59
+ parseFunctionString(str) {
60
+ // Regular expression to match function name and its arguments
61
+ const regex = /((?:[a-zA-Z_$][\w$]*\.)*)([a-zA-Z_$][\w$]*)\(([^]*)\)/
62
+
63
+ // Executing the regex to get the match
64
+ const match = regex.exec(str)
65
+
66
+ if (!match) {
67
+ // If the string doesn't match the expected format
68
+ return null
69
+ }
70
+
71
+ // Extracting function name
72
+ const namespaces = match[1] || ''
73
+ const functionName = namespaces + match[2]
74
+
75
+ // Extracting arguments
76
+ const argsStr = match[3].trim()
77
+
78
+ let args = []
79
+
80
+ if (argsStr !== '') {
81
+ let argBuffer = ''
82
+ let parenCount = 0
83
+ for (const element of argsStr) {
84
+ if (element === '(') parenCount++
85
+ else if (element === ')') parenCount--
86
+
87
+ if (element === ',' && parenCount === 0) {
88
+ args.push(argBuffer.trim())
89
+ argBuffer = ''
90
+ } else {
91
+ argBuffer += element
92
+ }
93
+ }
94
+ args.push(argBuffer.trim())
95
+ }
96
+
97
+ // Convert string representation of boolean, numbers, arrays, and objects to their actual types
98
+ args = args.map(arg => {
99
+ if (arg === 'true') return true
100
+ else if (arg === 'false') return false
101
+ else if (!isNaN(arg)) return parseFloat(arg)
102
+ else if (arg === 'null') return null
103
+ else if (arg.startsWith('[') && arg.endsWith(']')) {
104
+ try {
105
+ return JSON.parse(arg)
106
+ } catch(err) {
107
+ return ''
108
+ }
109
+ } else if (isStrLikelyEncoded(arg)) {
110
+ try{
111
+ return JSON.parse(decodeURIComponent(arg).replace(/^['"]|['"]$/g, ''))
112
+ } catch(err) {
113
+ return ''
114
+ }
115
+ }
116
+ return arg.replace(/^['"]|['"]$/g, '') // Remove surrounding quotes if any
117
+ })
118
+
119
+ return { functionName, args }
120
+ }
121
+
12
122
  /**
13
123
  * Updates the url with the given parameter and value
14
124
  * @param {String} uri
@@ -493,10 +603,32 @@ class YeldaChat {
493
603
  if (!this.configurationData.hasOwnProperty('canBeClosed') || this.configurationData.canBeClosed) {
494
604
  this.addAssistantBubbleText(eventData.text, 'y_notification')
495
605
  }
496
- break;
606
+ break
607
+ case config.FRAME_EVENT_TYPES.RECEIVED.EXEC_JS_FUNC:
608
+ this.handleExecFunction(eventData)
609
+ break
497
610
  }
498
611
  }
499
612
 
613
+ /**
614
+ * Execute the JS function passed from the webchat window
615
+ * @param {Object} eventData - iframe data sent from webchat window
616
+ */
617
+ handleExecFunction(eventData) {
618
+ if (!eventData.hasOwnProperty('data')) {
619
+ return
620
+ }
621
+
622
+ const execJsFunc = eventData.data
623
+ const response = this.parseFunctionString(execJsFunc)
624
+
625
+ if (!response) {
626
+ return
627
+ }
628
+
629
+ executeFunctionByName(response.functionName, window, response.args)
630
+ }
631
+
500
632
  /**
501
633
  * Create DynamicElements & Opens Light Gallery from the received mediaSources
502
634
  * @param {Object} data - iframe data sent from webchat window