@sensefolks/fastpoll 0.1.0

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.
Files changed (107) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/LICENSE +21 -0
  3. package/dist/cjs/app-globals-V2Kpy_OQ.js +8 -0
  4. package/dist/cjs/app-globals-V2Kpy_OQ.js.map +1 -0
  5. package/dist/cjs/index-CC5IS5t8.js +1437 -0
  6. package/dist/cjs/index-CC5IS5t8.js.map +1 -0
  7. package/dist/cjs/index-D8TNlmQq.js +201 -0
  8. package/dist/cjs/index-D8TNlmQq.js.map +1 -0
  9. package/dist/cjs/index.cjs.js +11 -0
  10. package/dist/cjs/index.cjs.js.map +1 -0
  11. package/dist/cjs/loader.cjs.js +16 -0
  12. package/dist/cjs/loader.cjs.js.map +1 -0
  13. package/dist/cjs/sf-fastpoll.cjs.entry.js +395 -0
  14. package/dist/cjs/sf-fastpoll.cjs.entry.js.map +1 -0
  15. package/dist/cjs/sf-fastpoll.cjs.js +28 -0
  16. package/dist/cjs/sf-fastpoll.cjs.js.map +1 -0
  17. package/dist/cjs/sf-fastpoll.entry.cjs.js.map +1 -0
  18. package/dist/collection/collection-manifest.json +12 -0
  19. package/dist/collection/components/sf-fastpoll/sf-fastpoll.css +76 -0
  20. package/dist/collection/components/sf-fastpoll/sf-fastpoll.js +454 -0
  21. package/dist/collection/components/sf-fastpoll/sf-fastpoll.js.map +1 -0
  22. package/dist/collection/index.js +11 -0
  23. package/dist/collection/index.js.map +1 -0
  24. package/dist/collection/utils/utils.js +189 -0
  25. package/dist/collection/utils/utils.js.map +1 -0
  26. package/dist/components/index.d.ts +33 -0
  27. package/dist/components/index.js +1427 -0
  28. package/dist/components/index.js.map +1 -0
  29. package/dist/components/sf-fastpoll.d.ts +11 -0
  30. package/dist/components/sf-fastpoll.js +425 -0
  31. package/dist/components/sf-fastpoll.js.map +1 -0
  32. package/dist/esm/app-globals-DQuL1Twl.js +6 -0
  33. package/dist/esm/app-globals-DQuL1Twl.js.map +1 -0
  34. package/dist/esm/index-CfdIRf0W.js +193 -0
  35. package/dist/esm/index-CfdIRf0W.js.map +1 -0
  36. package/dist/esm/index-XYfqntZe.js +1428 -0
  37. package/dist/esm/index-XYfqntZe.js.map +1 -0
  38. package/dist/esm/index.js +4 -0
  39. package/dist/esm/index.js.map +1 -0
  40. package/dist/esm/loader.js +14 -0
  41. package/dist/esm/loader.js.map +1 -0
  42. package/dist/esm/polyfills/core-js.js +11 -0
  43. package/dist/esm/polyfills/dom.js +79 -0
  44. package/dist/esm/polyfills/es5-html-element.js +1 -0
  45. package/dist/esm/polyfills/index.js +34 -0
  46. package/dist/esm/polyfills/system.js +6 -0
  47. package/dist/esm/sf-fastpoll.entry.js +393 -0
  48. package/dist/esm/sf-fastpoll.entry.js.map +1 -0
  49. package/dist/esm/sf-fastpoll.js +24 -0
  50. package/dist/esm/sf-fastpoll.js.map +1 -0
  51. package/dist/esm-es5/app-globals-DQuL1Twl.js +2 -0
  52. package/dist/esm-es5/app-globals-DQuL1Twl.js.map +1 -0
  53. package/dist/esm-es5/index-CfdIRf0W.js +2 -0
  54. package/dist/esm-es5/index-CfdIRf0W.js.map +1 -0
  55. package/dist/esm-es5/index-XYfqntZe.js +3 -0
  56. package/dist/esm-es5/index-XYfqntZe.js.map +1 -0
  57. package/dist/esm-es5/index.js +2 -0
  58. package/dist/esm-es5/index.js.map +1 -0
  59. package/dist/esm-es5/loader.js +2 -0
  60. package/dist/esm-es5/loader.js.map +1 -0
  61. package/dist/esm-es5/sf-fastpoll.entry.js +2 -0
  62. package/dist/esm-es5/sf-fastpoll.entry.js.map +1 -0
  63. package/dist/esm-es5/sf-fastpoll.js +2 -0
  64. package/dist/esm-es5/sf-fastpoll.js.map +1 -0
  65. package/dist/index.cjs.js +1 -0
  66. package/dist/index.js +1 -0
  67. package/dist/sf-fastpoll/index.esm.js +2 -0
  68. package/dist/sf-fastpoll/index.esm.js.map +1 -0
  69. package/dist/sf-fastpoll/loader.esm.js.map +1 -0
  70. package/dist/sf-fastpoll/p-1f6dca2a.system.entry.js +2 -0
  71. package/dist/sf-fastpoll/p-1f6dca2a.system.entry.js.map +1 -0
  72. package/dist/sf-fastpoll/p-4648bca3.entry.js +2 -0
  73. package/dist/sf-fastpoll/p-4648bca3.entry.js.map +1 -0
  74. package/dist/sf-fastpoll/p-BbPAtVJG.system.js +2 -0
  75. package/dist/sf-fastpoll/p-BbPAtVJG.system.js.map +1 -0
  76. package/dist/sf-fastpoll/p-C7EMppj8.system.js.map +1 -0
  77. package/dist/sf-fastpoll/p-C9ESvisV.system.js +3 -0
  78. package/dist/sf-fastpoll/p-C9ESvisV.system.js.map +1 -0
  79. package/dist/sf-fastpoll/p-CfdIRf0W.js +2 -0
  80. package/dist/sf-fastpoll/p-CfdIRf0W.js.map +1 -0
  81. package/dist/sf-fastpoll/p-CpmSDeqe.system.js +2 -0
  82. package/dist/sf-fastpoll/p-CpmSDeqe.system.js.map +1 -0
  83. package/dist/sf-fastpoll/p-DQuL1Twl.js +2 -0
  84. package/dist/sf-fastpoll/p-DQuL1Twl.js.map +1 -0
  85. package/dist/sf-fastpoll/p-JC66e5NR.system.js.map +1 -0
  86. package/dist/sf-fastpoll/p-S-cJYJS7.system.js +2 -0
  87. package/dist/sf-fastpoll/p-S-cJYJS7.system.js.map +1 -0
  88. package/dist/sf-fastpoll/p-XYfqntZe.js +3 -0
  89. package/dist/sf-fastpoll/p-XYfqntZe.js.map +1 -0
  90. package/dist/sf-fastpoll/p-zRZYYxiz.system.js +2 -0
  91. package/dist/sf-fastpoll/p-zRZYYxiz.system.js.map +1 -0
  92. package/dist/sf-fastpoll/sf-fastpoll.entry.esm.js.map +1 -0
  93. package/dist/sf-fastpoll/sf-fastpoll.esm.js +2 -0
  94. package/dist/sf-fastpoll/sf-fastpoll.esm.js.map +1 -0
  95. package/dist/sf-fastpoll/sf-fastpoll.js +127 -0
  96. package/dist/types/components/sf-fastpoll/sf-fastpoll.d.ts +77 -0
  97. package/dist/types/components.d.ts +47 -0
  98. package/dist/types/index.d.ts +11 -0
  99. package/dist/types/stencil-public-runtime.d.ts +1709 -0
  100. package/dist/types/utils/utils.d.ts +86 -0
  101. package/loader/cdn.js +2 -0
  102. package/loader/index.cjs.js +2 -0
  103. package/loader/index.d.ts +24 -0
  104. package/loader/index.es2017.js +2 -0
  105. package/loader/index.js +3 -0
  106. package/package.json +86 -0
  107. package/readme.md +239 -0
@@ -0,0 +1,201 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Utility functions for sf-fastpoll
5
+ */
6
+ /**
7
+ * Maximum allowed length for text inputs
8
+ */
9
+ const MAX_TEXT_LENGTH = 500;
10
+ const MAX_EMAIL_LENGTH = 254;
11
+ /**
12
+ * Sanitize user input to prevent XSS and limit length
13
+ * @param input The raw user input
14
+ * @param maxLength Maximum allowed length (default: 500)
15
+ * @returns Sanitized string
16
+ */
17
+ function sanitizeInput(input, maxLength = MAX_TEXT_LENGTH) {
18
+ if (typeof input !== 'string') {
19
+ return '';
20
+ }
21
+ // Trim whitespace
22
+ let sanitized = input.replace(/^\s+|\s+$/g, '');
23
+ // Remove null bytes
24
+ sanitized = sanitized.replace(/\0/g, '');
25
+ // Encode HTML entities to prevent XSS
26
+ sanitized = sanitized.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#x27;');
27
+ // Truncate to max length
28
+ if (sanitized.length > maxLength) {
29
+ sanitized = sanitized.substring(0, maxLength);
30
+ }
31
+ return sanitized;
32
+ }
33
+ /**
34
+ * Sanitize email input with email-specific rules
35
+ * @param email The raw email input
36
+ * @returns Sanitized email string
37
+ */
38
+ function sanitizeEmail(email) {
39
+ if (typeof email !== 'string') {
40
+ return '';
41
+ }
42
+ // Trim and lowercase
43
+ let sanitized = email.replace(/^\s+|\s+$/g, '').toLowerCase();
44
+ // Remove null bytes and control characters
45
+ sanitized = sanitized.replace(/[\0\x00-\x1F\x7F]/g, '');
46
+ // Truncate to max email length (RFC 5321)
47
+ if (sanitized.length > MAX_EMAIL_LENGTH) {
48
+ sanitized = sanitized.substring(0, MAX_EMAIL_LENGTH);
49
+ }
50
+ return sanitized;
51
+ }
52
+ /**
53
+ * Sanitize a value based on field type
54
+ * @param value The raw value
55
+ * @param inputType The type of input field
56
+ * @returns Sanitized value
57
+ */
58
+ function sanitizeByType(value, inputType) {
59
+ switch (inputType) {
60
+ case 'email':
61
+ return sanitizeEmail(value);
62
+ case 'number':
63
+ // Only allow digits, decimal point, and minus sign
64
+ const numStr = String(value).replace(/[^0-9.\-]/g, '');
65
+ return numStr.substring(0, 20); // Reasonable max for numbers
66
+ case 'dropdown':
67
+ case 'radio':
68
+ // For select/radio, sanitize but be more permissive (values come from config)
69
+ return sanitizeInput(value, 100);
70
+ case 'checkbox':
71
+ // Checkbox values are comma-separated, sanitize each
72
+ return value
73
+ .split(',')
74
+ .map(v => sanitizeInput(v.replace(/^\s+|\s+$/g, ''), 100))
75
+ .join(', ');
76
+ default:
77
+ return sanitizeInput(value);
78
+ }
79
+ }
80
+ /**
81
+ * Check if a survey key is valid UUID
82
+ * @param key The survey key to validate
83
+ * @returns True if the key is a valid UUID, false otherwise
84
+ */
85
+ function isValidKey(key) {
86
+ if (typeof key !== 'string' || key.trim().length === 0) {
87
+ return false;
88
+ }
89
+ // UUID v4 regex pattern
90
+ const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
91
+ return uuidRegex.test(key.trim());
92
+ }
93
+ /**
94
+ * Format error messages for display
95
+ * @param error The error object or message
96
+ * @returns A formatted error message string
97
+ */
98
+ function formatErrorMessage(error) {
99
+ if (typeof error === 'string') {
100
+ return error;
101
+ }
102
+ if (error instanceof Error) {
103
+ return error.message;
104
+ }
105
+ return 'An unknown error occurred';
106
+ }
107
+ /**
108
+ * Browser-compatible helper to check if array contains a value
109
+ * @param array The array to search
110
+ * @param value The value to find
111
+ * @returns true if value is found in array
112
+ */
113
+ function arrayContains(array, value) {
114
+ for (let i = 0; i < array.length; i++) {
115
+ if (array[i] === value) {
116
+ return true;
117
+ }
118
+ }
119
+ return false;
120
+ }
121
+ /**
122
+ * Browser-compatible helper to trim and split comma-separated values
123
+ * @param value The comma-separated string
124
+ * @returns Array of trimmed values
125
+ */
126
+ function parseCommaSeparatedValues(value) {
127
+ if (!value) {
128
+ return [];
129
+ }
130
+ const parts = value.split(',');
131
+ const result = [];
132
+ for (let i = 0; i < parts.length; i++) {
133
+ const trimmed = parts[i].replace(/^\s+|\s+$/g, ''); // Manual trim for IE compatibility
134
+ if (trimmed) {
135
+ result.push(trimmed);
136
+ }
137
+ }
138
+ return result;
139
+ }
140
+ /**
141
+ * Get field configuration for rendering
142
+ * @param detail The respondent detail configuration
143
+ * @param value The current value of the field
144
+ * @returns Field configuration object
145
+ */
146
+ function getFieldConfig(detail, value) {
147
+ const inputType = detail.inputType || 'text';
148
+ const placeholder = detail.placeholder || 'Enter your ' + detail.label.toLowerCase();
149
+ const required = detail.required !== false;
150
+ return {
151
+ inputType: inputType,
152
+ placeholder: placeholder,
153
+ required: required,
154
+ options: detail.options || [],
155
+ defaultValue: detail.defaultValue,
156
+ hasOptions: detail.options && detail.options.length > 0,
157
+ selectedValues: parseCommaSeparatedValues(value),
158
+ fieldValue: detail.value,
159
+ currentValue: value || '',
160
+ };
161
+ }
162
+ /**
163
+ * Browser-compatible helper to add value to comma-separated list
164
+ * @param currentValue The current comma-separated string
165
+ * @param valueToAdd The value to add
166
+ * @returns Updated comma-separated string
167
+ */
168
+ function addToCommaSeparatedList(currentValue, valueToAdd) {
169
+ const values = parseCommaSeparatedValues(currentValue);
170
+ if (!arrayContains(values, valueToAdd)) {
171
+ values.push(valueToAdd);
172
+ }
173
+ return values.join(', ');
174
+ }
175
+ /**
176
+ * Browser-compatible helper to remove value from comma-separated list
177
+ * @param currentValue The current comma-separated string
178
+ * @param valueToRemove The value to remove
179
+ * @returns Updated comma-separated string
180
+ */
181
+ function removeFromCommaSeparatedList(currentValue, valueToRemove) {
182
+ const values = parseCommaSeparatedValues(currentValue);
183
+ const result = [];
184
+ for (let i = 0; i < values.length; i++) {
185
+ if (values[i] !== valueToRemove) {
186
+ result.push(values[i]);
187
+ }
188
+ }
189
+ return result.join(', ');
190
+ }
191
+
192
+ exports.addToCommaSeparatedList = addToCommaSeparatedList;
193
+ exports.formatErrorMessage = formatErrorMessage;
194
+ exports.getFieldConfig = getFieldConfig;
195
+ exports.isValidKey = isValidKey;
196
+ exports.removeFromCommaSeparatedList = removeFromCommaSeparatedList;
197
+ exports.sanitizeByType = sanitizeByType;
198
+ exports.sanitizeInput = sanitizeInput;
199
+ //# sourceMappingURL=index-D8TNlmQq.js.map
200
+
201
+ //# sourceMappingURL=index-D8TNlmQq.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-D8TNlmQq.js","sources":["src/utils/utils.ts"],"sourcesContent":["/**\n * Utility functions for sf-fastpoll\n */\n\n/**\n * Maximum allowed length for text inputs\n */\nconst MAX_TEXT_LENGTH = 500;\nconst MAX_EMAIL_LENGTH = 254;\n\n/**\n * Sanitize user input to prevent XSS and limit length\n * @param input The raw user input\n * @param maxLength Maximum allowed length (default: 500)\n * @returns Sanitized string\n */\nexport function sanitizeInput(input: string, maxLength: number = MAX_TEXT_LENGTH): string {\n if (typeof input !== 'string') {\n return '';\n }\n\n // Trim whitespace\n let sanitized = input.replace(/^\\s+|\\s+$/g, '');\n\n // Remove null bytes\n sanitized = sanitized.replace(/\\0/g, '');\n\n // Encode HTML entities to prevent XSS\n sanitized = sanitized.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;').replace(/'/g, '&#x27;');\n\n // Truncate to max length\n if (sanitized.length > maxLength) {\n sanitized = sanitized.substring(0, maxLength);\n }\n\n return sanitized;\n}\n\n/**\n * Sanitize email input with email-specific rules\n * @param email The raw email input\n * @returns Sanitized email string\n */\nexport function sanitizeEmail(email: string): string {\n if (typeof email !== 'string') {\n return '';\n }\n\n // Trim and lowercase\n let sanitized = email.replace(/^\\s+|\\s+$/g, '').toLowerCase();\n\n // Remove null bytes and control characters\n sanitized = sanitized.replace(/[\\0\\x00-\\x1F\\x7F]/g, '');\n\n // Truncate to max email length (RFC 5321)\n if (sanitized.length > MAX_EMAIL_LENGTH) {\n sanitized = sanitized.substring(0, MAX_EMAIL_LENGTH);\n }\n\n return sanitized;\n}\n\n/**\n * Sanitize a value based on field type\n * @param value The raw value\n * @param inputType The type of input field\n * @returns Sanitized value\n */\nexport function sanitizeByType(value: string, inputType: string): string {\n switch (inputType) {\n case 'email':\n return sanitizeEmail(value);\n case 'number':\n // Only allow digits, decimal point, and minus sign\n const numStr = String(value).replace(/[^0-9.\\-]/g, '');\n return numStr.substring(0, 20); // Reasonable max for numbers\n case 'dropdown':\n case 'radio':\n // For select/radio, sanitize but be more permissive (values come from config)\n return sanitizeInput(value, 100);\n case 'checkbox':\n // Checkbox values are comma-separated, sanitize each\n return value\n .split(',')\n .map(v => sanitizeInput(v.replace(/^\\s+|\\s+$/g, ''), 100))\n .join(', ');\n default:\n return sanitizeInput(value);\n }\n}\n\n/**\n * Interface for respondent detail options\n */\ninterface RespondentDetailOption {\n value: string;\n label: string;\n}\n\n/**\n * Interface for respondent detail configuration\n */\ninterface RespondentDetail {\n label: string;\n value: string;\n inputType: string;\n required?: boolean;\n placeholder?: string;\n options?: RespondentDetailOption[];\n defaultValue?: any;\n}\n\n/**\n * Check if a survey key is valid UUID\n * @param key The survey key to validate\n * @returns True if the key is a valid UUID, false otherwise\n */\nexport function isValidKey(key: string | undefined | null): boolean {\n if (typeof key !== 'string' || key.trim().length === 0) {\n return false;\n }\n\n // UUID v4 regex pattern\n const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\n return uuidRegex.test(key.trim());\n}\n\n/**\n * Format error messages for display\n * @param error The error object or message\n * @returns A formatted error message string\n */\nexport function formatErrorMessage(error: any): string {\n if (typeof error === 'string') {\n return error;\n }\n\n if (error instanceof Error) {\n return error.message;\n }\n\n return 'An unknown error occurred';\n}\n\n/**\n * Browser-compatible helper to check if array contains a value\n * @param array The array to search\n * @param value The value to find\n * @returns true if value is found in array\n */\nfunction arrayContains(array: string[], value: string): boolean {\n for (let i = 0; i < array.length; i++) {\n if (array[i] === value) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Browser-compatible helper to trim and split comma-separated values\n * @param value The comma-separated string\n * @returns Array of trimmed values\n */\nfunction parseCommaSeparatedValues(value: string): string[] {\n if (!value) {\n return [];\n }\n const parts = value.split(',');\n const result = [];\n for (let i = 0; i < parts.length; i++) {\n const trimmed = parts[i].replace(/^\\s+|\\s+$/g, ''); // Manual trim for IE compatibility\n if (trimmed) {\n result.push(trimmed);\n }\n }\n return result;\n}\n\n/**\n * Get field configuration for rendering\n * @param detail The respondent detail configuration\n * @param value The current value of the field\n * @returns Field configuration object\n */\nexport function getFieldConfig(detail: RespondentDetail, value: string) {\n const inputType = detail.inputType || 'text';\n const placeholder = detail.placeholder || 'Enter your ' + detail.label.toLowerCase();\n const required = detail.required !== false;\n\n return {\n inputType: inputType,\n placeholder: placeholder,\n required: required,\n options: detail.options || [],\n defaultValue: detail.defaultValue,\n hasOptions: detail.options && detail.options.length > 0,\n selectedValues: parseCommaSeparatedValues(value),\n fieldValue: detail.value,\n currentValue: value || '',\n };\n}\n\n/**\n * Browser-compatible helper to add value to comma-separated list\n * @param currentValue The current comma-separated string\n * @param valueToAdd The value to add\n * @returns Updated comma-separated string\n */\nexport function addToCommaSeparatedList(currentValue: string, valueToAdd: string): string {\n const values = parseCommaSeparatedValues(currentValue);\n if (!arrayContains(values, valueToAdd)) {\n values.push(valueToAdd);\n }\n return values.join(', ');\n}\n\n/**\n * Browser-compatible helper to remove value from comma-separated list\n * @param currentValue The current comma-separated string\n * @param valueToRemove The value to remove\n * @returns Updated comma-separated string\n */\nexport function removeFromCommaSeparatedList(currentValue: string, valueToRemove: string): string {\n const values = parseCommaSeparatedValues(currentValue);\n const result = [];\n for (let i = 0; i < values.length; i++) {\n if (values[i] !== valueToRemove) {\n result.push(values[i]);\n }\n }\n return result.join(', ');\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ 'use strict';
2
+
3
+ var index = require('./index-D8TNlmQq.js');
4
+
5
+
6
+
7
+ exports.formatErrorMessage = index.formatErrorMessage;
8
+ exports.isValidKey = index.isValidKey;
9
+ //# sourceMappingURL=index.cjs.js.map
10
+
11
+ //# sourceMappingURL=index.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;"}
@@ -0,0 +1,16 @@
1
+ 'use strict';
2
+
3
+ var index = require('./index-CC5IS5t8.js');
4
+ var appGlobals = require('./app-globals-V2Kpy_OQ.js');
5
+
6
+ const defineCustomElements = async (win, options) => {
7
+ if (typeof window === 'undefined') return undefined;
8
+ await appGlobals.globalScripts();
9
+ return index.bootstrapLazy([["sf-fastpoll.cjs",[[1,"sf-fastpoll",{"surveyKey":[1,"survey-key"],"completionMessage":[1,"completion-message"],"config":[32],"respondentDetails":[32],"loading":[32],"error":[32],"currentStep":[32],"selectedChoices":[32],"selectedFollowUpChoices":[32],"userRespondentDetails":[32],"submitted":[32],"announceMessage":[32],"formErrors":[32]}]]]], options);
10
+ };
11
+
12
+ exports.setNonce = index.setNonce;
13
+ exports.defineCustomElements = defineCustomElements;
14
+ //# sourceMappingURL=loader.cjs.js.map
15
+
16
+ //# sourceMappingURL=loader.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.cjs.js","sources":["@lazy-external-entrypoint?app-data=conditional"],"sourcesContent":["export { setNonce } from '@stencil/core';\nimport { bootstrapLazy } from '@stencil/core';\nimport { globalScripts } from '@stencil/core/internal/app-globals';\nexport const defineCustomElements = async (win, options) => {\n if (typeof window === 'undefined') return undefined;\n await globalScripts();\n return bootstrapLazy([/*!__STENCIL_LAZY_DATA__*/], options);\n};\n"],"names":["globalScripts","bootstrapLazy"],"mappings":";;;;;AAGY,MAAC,oBAAoB,GAAG,OAAO,GAAG,EAAE,OAAO,KAAK;AAC5D,EAAE,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,OAAO,SAAS;AACrD,EAAE,MAAMA,wBAAa,EAAE;AACvB,EAAE,OAAOC,mBAAa,CAAC,4BAA4B,EAAE,OAAO,CAAC;AAC7D;;;;;"}
@@ -0,0 +1,395 @@
1
+ 'use strict';
2
+
3
+ var index = require('./index-CC5IS5t8.js');
4
+ var index$1 = require('./index-D8TNlmQq.js');
5
+
6
+ const sfFastpollCss = ":host{display:block}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border:0}input:focus,select:focus,button:focus{outline:2px solid #005fcc;outline-offset:2px}[aria-invalid='true']{border-color:#d32f2f}[part='error-message']{color:#d32f2f;font-size:0.875rem;margin-top:0.25rem}[part='choice-option']{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;margin:0.5rem 0;cursor:pointer}[part='choice-option']:hover{background-color:rgba(0, 95, 204, 0.1)}@media (prefers-contrast: high){input:focus,select:focus,button:focus{outline:3px solid}[part='error-message']{background-color:#d32f2f;color:white;padding:0.25rem}[part='choice-option']:hover{background-color:highlight;color:highlighttext}}@media (prefers-reduced-motion: reduce){*{-webkit-animation-duration:0.01ms !important;animation-duration:0.01ms !important;-webkit-animation-iteration-count:1 !important;animation-iteration-count:1 !important;-webkit-transition-duration:0.01ms !important;transition-duration:0.01ms !important}}";
7
+
8
+ const SURVEY_API_ENDPOINT = index.Env.SURVEY_API_ENDPOINT;
9
+ const RESPONSE_API_ENDPOINT = index.Env.RESPONSE_API_ENDPOINT;
10
+ var SurveyStep;
11
+ (function (SurveyStep) {
12
+ SurveyStep[SurveyStep["POLL"] = 0] = "POLL";
13
+ SurveyStep[SurveyStep["FOLLOW_UP"] = 1] = "FOLLOW_UP";
14
+ SurveyStep[SurveyStep["RESPONDENT_DETAILS"] = 2] = "RESPONDENT_DETAILS";
15
+ SurveyStep[SurveyStep["COMPLETION"] = 3] = "COMPLETION";
16
+ })(SurveyStep || (SurveyStep = {}));
17
+ const SfFastpoll = class {
18
+ constructor(hostRef) {
19
+ index.registerInstance(this, hostRef);
20
+ this.completionMessage = 'Thank you for your response!';
21
+ this.config = null;
22
+ this.respondentDetails = [];
23
+ this.loading = false;
24
+ this.error = null;
25
+ this.currentStep = SurveyStep.POLL;
26
+ this.selectedChoices = [];
27
+ this.selectedFollowUpChoices = [];
28
+ this.userRespondentDetails = {};
29
+ this.submitted = false;
30
+ this.announceMessage = '';
31
+ this.formErrors = {};
32
+ this.surveyStartTime = 0;
33
+ }
34
+ componentWillLoad() {
35
+ if (index$1.isValidKey(this.surveyKey)) {
36
+ return this.fetchSurveyData();
37
+ }
38
+ return Promise.resolve();
39
+ }
40
+ async fetchSurveyData() {
41
+ this.loading = true;
42
+ this.error = null;
43
+ this.surveyStartTime = Date.now();
44
+ try {
45
+ const response = await fetch(`${SURVEY_API_ENDPOINT}/${this.surveyKey}`);
46
+ const data = await response.json();
47
+ if (!data.success) {
48
+ throw new Error(data.message);
49
+ }
50
+ this.config = data.payload.config;
51
+ this.respondentDetails = data.payload.respondentDetails || [];
52
+ }
53
+ catch (error) {
54
+ this.error = error instanceof Error ? error.message : String(error);
55
+ }
56
+ finally {
57
+ this.loading = false;
58
+ }
59
+ }
60
+ async retryOperation() {
61
+ if (this.config) {
62
+ // If we have config, retry submission
63
+ await this.submitResponse();
64
+ }
65
+ else {
66
+ // Otherwise, retry fetching survey data
67
+ await this.fetchSurveyData();
68
+ }
69
+ }
70
+ async submitResponse() {
71
+ if (!this.config || this.submitted) {
72
+ return;
73
+ }
74
+ this.loading = true;
75
+ this.error = null;
76
+ try {
77
+ const completionTimeSeconds = this.surveyStartTime > 0 ? Math.round((Date.now() - this.surveyStartTime) / 1000) : 0;
78
+ const userAgentInfo = this.getUserAgentInfo();
79
+ const submissionData = {
80
+ surveyPublicKey: this.surveyKey,
81
+ responseData: {
82
+ selectedChoices: this.selectedChoices,
83
+ selectedFollowUpChoices: this.selectedFollowUpChoices,
84
+ },
85
+ respondentDetails: this.userRespondentDetails,
86
+ userAgent: userAgentInfo,
87
+ completionTime: completionTimeSeconds,
88
+ surveyType: 'fastPoll',
89
+ };
90
+ const response = await fetch(`${RESPONSE_API_ENDPOINT}`, {
91
+ method: 'POST',
92
+ headers: {
93
+ 'Content-Type': 'application/json',
94
+ },
95
+ body: JSON.stringify(submissionData),
96
+ });
97
+ const result = await response.json();
98
+ if (!result.success) {
99
+ throw new Error(result.message);
100
+ }
101
+ this.submitted = true;
102
+ this.currentStep = SurveyStep.COMPLETION;
103
+ }
104
+ catch (error) {
105
+ this.error = error instanceof Error ? error.message : String(error);
106
+ }
107
+ finally {
108
+ this.loading = false;
109
+ }
110
+ }
111
+ getUserAgentInfo() {
112
+ // Use modern navigator.userAgentData when available, fallback to userAgent parsing
113
+ const getPlatform = () => {
114
+ var _a;
115
+ if ('userAgentData' in navigator && ((_a = navigator.userAgentData) === null || _a === void 0 ? void 0 : _a.platform)) {
116
+ return navigator.userAgentData.platform;
117
+ }
118
+ // Fallback: extract platform info from userAgent
119
+ const ua = navigator.userAgent;
120
+ if (ua.includes('Win'))
121
+ return 'Windows';
122
+ if (ua.includes('Mac'))
123
+ return 'macOS';
124
+ if (ua.includes('Linux'))
125
+ return 'Linux';
126
+ if (ua.includes('Android'))
127
+ return 'Android';
128
+ if (ua.includes('iOS'))
129
+ return 'iOS';
130
+ return 'Unknown';
131
+ };
132
+ return {
133
+ userAgent: navigator.userAgent,
134
+ language: navigator.language,
135
+ platform: getPlatform(),
136
+ cookieEnabled: navigator.cookieEnabled,
137
+ onLine: navigator.onLine,
138
+ screenResolution: `${screen.width}x${screen.height}`,
139
+ colorDepth: screen.colorDepth,
140
+ timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
141
+ timestamp: new Date().toISOString(),
142
+ };
143
+ }
144
+ nextStep() {
145
+ if (!this.validateForm()) {
146
+ return;
147
+ }
148
+ if (this.currentStep === SurveyStep.POLL) {
149
+ if (this.hasFollowUpStep()) {
150
+ this.currentStep = SurveyStep.FOLLOW_UP;
151
+ this.announceToScreenReader('Moving to follow-up question');
152
+ }
153
+ else if (this.hasRespondentDetailsStep()) {
154
+ this.currentStep = SurveyStep.RESPONDENT_DETAILS;
155
+ this.announceToScreenReader('Moving to respondent details');
156
+ }
157
+ else {
158
+ this.announceToScreenReader('Submitting poll...');
159
+ this.submitResponse();
160
+ }
161
+ }
162
+ else if (this.currentStep === SurveyStep.FOLLOW_UP) {
163
+ if (this.hasRespondentDetailsStep()) {
164
+ this.currentStep = SurveyStep.RESPONDENT_DETAILS;
165
+ this.announceToScreenReader('Moving to respondent details');
166
+ }
167
+ else {
168
+ this.announceToScreenReader('Submitting poll...');
169
+ this.submitResponse();
170
+ }
171
+ }
172
+ else if (this.currentStep === SurveyStep.RESPONDENT_DETAILS) {
173
+ this.announceToScreenReader('Submitting poll...');
174
+ this.submitResponse();
175
+ }
176
+ }
177
+ prevStep() {
178
+ if (this.currentStep === SurveyStep.FOLLOW_UP) {
179
+ this.currentStep = SurveyStep.POLL;
180
+ }
181
+ else if (this.currentStep === SurveyStep.RESPONDENT_DETAILS) {
182
+ if (this.hasFollowUpStep()) {
183
+ this.currentStep = SurveyStep.FOLLOW_UP;
184
+ }
185
+ else {
186
+ this.currentStep = SurveyStep.POLL;
187
+ }
188
+ }
189
+ }
190
+ hasFollowUpStep() {
191
+ var _a, _b, _c;
192
+ return !!(((_a = this.config) === null || _a === void 0 ? void 0 : _a.followUpQuestion) && ((_c = (_b = this.config) === null || _b === void 0 ? void 0 : _b.followUpChoices) === null || _c === void 0 ? void 0 : _c.length));
193
+ }
194
+ hasRespondentDetailsStep() {
195
+ return this.respondentDetails.length > 0;
196
+ }
197
+ announceToScreenReader(message) {
198
+ this.announceMessage = message;
199
+ setTimeout(() => {
200
+ this.announceMessage = '';
201
+ }, 1000);
202
+ }
203
+ isValidEmail(email) {
204
+ // Basic email validation regex
205
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
206
+ return emailRegex.test(email.trim());
207
+ }
208
+ validateRespondentField(detail, value) {
209
+ if (detail.required !== false && (!value || value.trim().length === 0)) {
210
+ return 'This field is required';
211
+ }
212
+ if (value && value.trim().length > 0) {
213
+ const config = index$1.getFieldConfig(detail, value);
214
+ if (config.inputType === 'email') {
215
+ if (!this.isValidEmail(value)) {
216
+ return 'Please enter a valid email address';
217
+ }
218
+ }
219
+ }
220
+ return null;
221
+ }
222
+ validateForm() {
223
+ const errors = {};
224
+ if (this.currentStep === SurveyStep.POLL && this.selectedChoices.length === 0) {
225
+ errors['poll'] = 'Please select at least one option';
226
+ }
227
+ if (this.currentStep === SurveyStep.FOLLOW_UP && this.selectedFollowUpChoices.length === 0) {
228
+ errors['followup'] = 'Please select at least one option';
229
+ }
230
+ this.formErrors = errors;
231
+ return Object.keys(errors).length === 0;
232
+ }
233
+ handleChoiceSelect(choice) {
234
+ var _a;
235
+ const choiceValue = choice.value;
236
+ const isMultipleChoice = ((_a = this.config) === null || _a === void 0 ? void 0 : _a.choiceType) === 'multiChoice';
237
+ if (isMultipleChoice) {
238
+ if (this.selectedChoices.includes(choiceValue)) {
239
+ this.selectedChoices = this.selectedChoices.filter(c => c !== choiceValue);
240
+ this.announceToScreenReader(`Deselected: ${choice.label}`);
241
+ }
242
+ else {
243
+ this.selectedChoices = [...this.selectedChoices, choiceValue];
244
+ this.announceToScreenReader(`Selected: ${choice.label}`);
245
+ }
246
+ }
247
+ else {
248
+ this.selectedChoices = [choiceValue];
249
+ this.announceToScreenReader(`Selected: ${choice.label}`);
250
+ }
251
+ // Clear any validation errors when user makes a selection
252
+ if (this.formErrors['poll']) {
253
+ const errors = Object.assign({}, this.formErrors);
254
+ delete errors['poll'];
255
+ this.formErrors = errors;
256
+ }
257
+ }
258
+ handleFollowUpChoiceSelect(choice) {
259
+ const choiceValue = choice.value;
260
+ if (this.selectedFollowUpChoices.includes(choiceValue)) {
261
+ this.selectedFollowUpChoices = this.selectedFollowUpChoices.filter(c => c !== choiceValue);
262
+ }
263
+ else {
264
+ this.selectedFollowUpChoices = [...this.selectedFollowUpChoices, choiceValue];
265
+ }
266
+ }
267
+ handleRespondentDetailChange(key, event) {
268
+ const target = event.target;
269
+ const detail = this.respondentDetails.find(d => d.value === key);
270
+ const inputType = (detail === null || detail === void 0 ? void 0 : detail.inputType) || 'text';
271
+ let value;
272
+ if (target.type === 'checkbox') {
273
+ // Handle checkbox inputs - maintain comma-separated list using browser-compatible helpers
274
+ const currentValue = this.userRespondentDetails[key] || '';
275
+ const sanitizedCheckboxValue = index$1.sanitizeInput(target.value, 100);
276
+ if (target.checked) {
277
+ value = index$1.addToCommaSeparatedList(currentValue, sanitizedCheckboxValue);
278
+ }
279
+ else {
280
+ value = index$1.removeFromCommaSeparatedList(currentValue, sanitizedCheckboxValue);
281
+ }
282
+ }
283
+ else {
284
+ // Handle all other input types (text, email, number, radio, select)
285
+ // Sanitize based on field type
286
+ value = index$1.sanitizeByType(target.value, inputType);
287
+ }
288
+ this.userRespondentDetails = Object.assign(Object.assign({}, this.userRespondentDetails), { [key]: value });
289
+ }
290
+ createInputHandler(fieldValue) {
291
+ const self = this;
292
+ return function (e) {
293
+ self.handleRespondentDetailChange(fieldValue, e);
294
+ };
295
+ }
296
+ isValueInArray(array, value) {
297
+ for (let i = 0; i < array.length; i++) {
298
+ if (array[i] === value) {
299
+ return true;
300
+ }
301
+ }
302
+ return false;
303
+ }
304
+ renderField(detail) {
305
+ const config = index$1.getFieldConfig(detail, this.userRespondentDetails[detail.value] || '');
306
+ const inputHandler = this.createInputHandler(detail.value);
307
+ switch (config.inputType) {
308
+ case 'text':
309
+ case 'email':
310
+ case 'number':
311
+ return (index.h("input", { part: "input form-input", type: config.inputType, value: config.currentValue, onInput: inputHandler, placeholder: config.placeholder, required: config.required }));
312
+ case 'dropdown':
313
+ if (!config.hasOptions) {
314
+ return index.h("input", { part: "input form-input", type: "text", value: config.currentValue, onInput: inputHandler, placeholder: config.placeholder, required: config.required });
315
+ }
316
+ return (index.h("select", { part: "select form-select", onChange: inputHandler, required: config.required }, !config.defaultValue && (index.h("option", { value: "", disabled: true }, config.placeholder)), config.options.map(function (option) {
317
+ return (index.h("option", { key: option.value, value: option.value, selected: config.currentValue === option.value || (!config.currentValue && config.defaultValue === option.value) }, option.label));
318
+ })));
319
+ case 'radio':
320
+ if (!config.hasOptions) {
321
+ return index.h("input", { part: "input form-input", type: "text", value: config.currentValue, onInput: inputHandler, placeholder: config.placeholder, required: config.required });
322
+ }
323
+ return (index.h("div", { part: "radio-group" }, config.options.map(function (option) {
324
+ return (index.h("div", { key: option.value, part: "radio-option" }, index.h("input", { part: "radio-input", type: "radio", id: config.fieldValue + '-' + option.value, name: config.fieldValue, value: option.value, checked: config.currentValue === option.value || (!config.currentValue && config.defaultValue === option.value), onChange: inputHandler, required: config.required }), index.h("label", { part: "radio-label", htmlFor: config.fieldValue + '-' + option.value }, option.label)));
325
+ })));
326
+ case 'checkbox':
327
+ if (!config.hasOptions) {
328
+ return index.h("input", { part: "input form-input", type: "text", value: config.currentValue, onInput: inputHandler, placeholder: config.placeholder, required: config.required });
329
+ }
330
+ const self = this;
331
+ return (index.h("div", { part: "checkbox-group" }, config.options.map(function (option) {
332
+ return (index.h("div", { key: option.value, part: "checkbox-option" }, index.h("input", { part: "checkbox-input", type: "checkbox", id: config.fieldValue + '-' + option.value, name: config.fieldValue, value: option.value, checked: self.isValueInArray(config.selectedValues, option.value), onChange: inputHandler }), index.h("label", { part: "checkbox-label", htmlFor: config.fieldValue + '-' + option.value }, option.label)));
333
+ })));
334
+ default:
335
+ return index.h("input", { part: "input form-input", type: "text", value: config.currentValue, onInput: inputHandler, placeholder: config.placeholder, required: config.required });
336
+ }
337
+ }
338
+ isRespondentDetailsValid() {
339
+ return this.respondentDetails.every(detail => {
340
+ const value = this.userRespondentDetails[detail.value] || '';
341
+ const error = this.validateRespondentField(detail, value);
342
+ return error === null;
343
+ });
344
+ }
345
+ renderPollStep() {
346
+ var _a, _b, _c, _d;
347
+ const isFinalStep = !this.hasFollowUpStep() && !this.hasRespondentDetailsStep();
348
+ const isMultiple = ((_a = this.config) === null || _a === void 0 ? void 0 : _a.choiceType) === 'multiChoice';
349
+ const hasError = !!this.formErrors['poll'];
350
+ return (index.h("div", { part: "step poll-step" }, index.h("h2", { part: "heading poll-heading", id: "poll-heading" }, ((_b = this.config) === null || _b === void 0 ? void 0 : _b.question) || 'What is your choice?'), index.h("div", { part: "choices-container", role: isMultiple ? 'group' : 'radiogroup', "aria-labelledby": "poll-heading", "aria-invalid": hasError, "aria-describedby": hasError ? 'poll-error' : undefined }, (_d = (_c = this.config) === null || _c === void 0 ? void 0 : _c.choices) === null || _d === void 0 ? void 0 : _d.map((choice, index$1) => (index.h("label", { part: "choice-option", key: choice.value }, index.h("input", { part: isMultiple ? 'checkbox-input' : 'radio-input', type: isMultiple ? 'checkbox' : 'radio', id: `poll-choice-${index$1}`, name: "poll-choice", value: choice.value, checked: this.selectedChoices.includes(choice.value), onChange: () => this.handleChoiceSelect(choice), "aria-describedby": `poll-choice-${index$1}-label` }), index.h("span", { part: "choice-label", id: `poll-choice-${index$1}-label` }, choice.label))))), hasError && (index.h("div", { part: "error-message", id: "poll-error", role: "alert", "aria-live": "polite" }, this.formErrors['poll'])), index.h("div", { part: "button-container" }, index.h("button", { part: "button next-button", onClick: () => this.nextStep(), disabled: this.selectedChoices.length === 0 }, isFinalStep ? 'Submit' : 'Next'))));
351
+ }
352
+ renderFollowUpStep() {
353
+ var _a, _b, _c;
354
+ const isFinalStep = !this.hasRespondentDetailsStep();
355
+ return (index.h("div", { part: "step follow-up-step" }, index.h("h2", { part: "heading follow-up-heading" }, ((_a = this.config) === null || _a === void 0 ? void 0 : _a.followUpQuestion) || 'Follow-up question'), index.h("div", { part: "choices-container" }, (_c = (_b = this.config) === null || _b === void 0 ? void 0 : _b.followUpChoices) === null || _c === void 0 ? void 0 : _c.map(choice => (index.h("label", { part: "choice-option", key: choice.value }, index.h("input", { part: "checkbox-input", type: "checkbox", name: "follow-up-choice", value: choice.value, checked: this.selectedFollowUpChoices.includes(choice.value), onChange: () => this.handleFollowUpChoiceSelect(choice) }), index.h("span", { part: "choice-label" }, choice.label))))), index.h("div", { part: "button-container" }, index.h("button", { part: "button back-button", onClick: () => this.prevStep() }, "Back"), index.h("button", { part: "button next-button", onClick: () => this.nextStep() }, isFinalStep ? 'Submit' : 'Next'))));
356
+ }
357
+ renderRespondentDetailsStep() {
358
+ return (index.h("div", { part: "step respondent-details-step" }, index.h("h2", { part: "heading respondent-details-heading" }, "Tell us about yourself"), index.h("div", { part: "form-container" }, this.respondentDetails.map(detail => (index.h("div", { part: "form-field" }, index.h("label", { part: "form-label" }, detail.label, detail.required && index.h("span", { part: "required-indicator" }, " *")), this.renderField(detail))))), index.h("div", { part: "button-container" }, index.h("button", { part: "button back-button", onClick: () => this.prevStep() }, "Back"), index.h("button", { part: "button submit-button", onClick: () => this.submitResponse(), disabled: !this.isRespondentDetailsValid() }, "Submit"))));
359
+ }
360
+ renderCompletionStep() {
361
+ return (index.h("div", { part: "step completion-step" }, index.h("h2", { part: "heading completion-heading" }, this.completionMessage)));
362
+ }
363
+ renderCurrentStep() {
364
+ const stepRenderers = {
365
+ [SurveyStep.POLL]: this.renderPollStep.bind(this),
366
+ [SurveyStep.FOLLOW_UP]: this.renderFollowUpStep.bind(this),
367
+ [SurveyStep.RESPONDENT_DETAILS]: this.renderRespondentDetailsStep.bind(this),
368
+ [SurveyStep.COMPLETION]: this.renderCompletionStep.bind(this),
369
+ };
370
+ const renderer = stepRenderers[this.currentStep] || stepRenderers[SurveyStep.POLL];
371
+ return renderer();
372
+ }
373
+ render() {
374
+ if (!index$1.isValidKey(this.surveyKey)) {
375
+ return (index.h(index.Host, null, index.h("p", { part: "message error-message" }, "Unable to render survey due to invalid public key")));
376
+ }
377
+ if (this.loading) {
378
+ return (index.h(index.Host, null, index.h("p", { part: "message loading-message" }, "Loading survey...")));
379
+ }
380
+ if (this.error) {
381
+ return (index.h(index.Host, null, index.h("div", { part: "error-container" }, index.h("p", { part: "message error-message" }, this.error), index.h("button", { part: "button retry-button", onClick: () => this.retryOperation() }, "Try again"))));
382
+ }
383
+ if (!this.config) {
384
+ return (index.h(index.Host, null, index.h("div", { part: "error-container" }, index.h("p", { part: "message error-message" }, "No survey configuration found"), index.h("button", { part: "button retry-button", onClick: () => this.retryOperation() }, "Try again"))));
385
+ }
386
+ return (index.h(index.Host, null, index.h("div", { part: "survey-container", role: "main", "aria-label": "Poll Survey" }, this.renderCurrentStep(), index.h("div", { "aria-live": "polite", "aria-atomic": "true", class: "sr-only", part: "announcements" }, this.announceMessage))));
387
+ }
388
+ get el() { return index.getElement(this); }
389
+ };
390
+ SfFastpoll.style = sfFastpollCss;
391
+
392
+ exports.sf_fastpoll = SfFastpoll;
393
+ //# sourceMappingURL=sf-fastpoll.entry.cjs.js.map
394
+
395
+ //# sourceMappingURL=sf-fastpoll.cjs.entry.js.map