@bliptarjs/sdk 0.2.0 → 0.3.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.
- package/README.md +144 -407
- package/dist/index.cjs +16 -16
- package/dist/index.d.cts +11 -2
- package/dist/index.d.ts +11 -2
- package/dist/index.js +15 -15
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,19 +1,44 @@
|
|
|
1
1
|
# @bliptarjs/sdk
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Bliptar** – Micro-feedback that users actually respond to.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## The Problem
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
npm install @bliptarjs/sdk
|
|
9
|
-
```
|
|
7
|
+
Users abandon your app, churn from your service, and leave features unused – without ever telling you why. Traditional surveys are too long. Pop-ups are annoying. You're left guessing.
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
## The Solution
|
|
10
|
+
|
|
11
|
+
Bliptar captures real-time user sentiment with **nano feedback forms** – quick 1-2 question surveys that appear at exactly the right moment. After checkout, on exit intent, when they've scrolled 80% of your pricing page, or 30 seconds into using a new feature.
|
|
12
|
+
|
|
13
|
+
**One question. Two seconds. Real insights.**
|
|
14
|
+
|
|
15
|
+
### Form Types
|
|
16
|
+
- **Thumbs up/down** – "Was this helpful?"
|
|
17
|
+
- **Emoji scales** – "How was your experience?"
|
|
18
|
+
- **Star ratings** – "Rate this feature"
|
|
19
|
+
- **NPS** – "How likely are you to recommend us?"
|
|
20
|
+
- **Single choice** – Quick multiple choice
|
|
21
|
+
- **Text input** – Open-ended feedback
|
|
22
|
+
|
|
23
|
+
### Smart Triggers
|
|
24
|
+
- **Page view** – When user lands on a page
|
|
25
|
+
- **Scroll depth** – When user scrolls to X%
|
|
26
|
+
- **Time on page** – After X seconds
|
|
27
|
+
- **Exit intent** – When user is about to leave
|
|
28
|
+
- **Custom events** – After checkout, signup, or any action
|
|
29
|
+
|
|
30
|
+
### Why Bliptar?
|
|
31
|
+
- **Dashboard controlled** – No code changes to update campaigns
|
|
32
|
+
- **Lightweight SDK** – ~8KB gzipped, zero dependencies
|
|
33
|
+
- **Works everywhere** – React, Next.js, Vue, or vanilla JS
|
|
34
|
+
- **Privacy first** – No cookies, GDPR friendly
|
|
35
|
+
|
|
36
|
+
**Stop guessing how users feel. Start knowing.**
|
|
37
|
+
|
|
38
|
+
## Installation
|
|
14
39
|
|
|
15
40
|
```bash
|
|
16
|
-
|
|
41
|
+
npm install @bliptarjs/sdk
|
|
17
42
|
```
|
|
18
43
|
|
|
19
44
|
## Quick Start
|
|
@@ -28,397 +53,148 @@ const bliptar = createBliptar({
|
|
|
28
53
|
bliptar.init();
|
|
29
54
|
```
|
|
30
55
|
|
|
31
|
-
That's it! The SDK
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
- **Page View** - When a user visits a page
|
|
38
|
-
- **Button Click** - When a user clicks a specific element
|
|
39
|
-
- **Scroll Depth** - When a user scrolls to a certain percentage
|
|
40
|
-
- **Time on Page** - After a user spends time on a page
|
|
41
|
-
- **Exit Intent** - When a user is about to leave
|
|
42
|
-
- **Custom Events** - Triggered programmatically via SDK
|
|
43
|
-
|
|
44
|
-
## API Reference
|
|
56
|
+
That's it! The SDK automatically:
|
|
57
|
+
- Fetches your active campaigns from the dashboard
|
|
58
|
+
- Sets up the appropriate triggers (page view, scroll depth, time on page, exit intent)
|
|
59
|
+
- Shows feedback forms when conditions are met
|
|
60
|
+
- Submits responses to your Bliptar dashboard
|
|
45
61
|
|
|
46
|
-
|
|
62
|
+
## How It Works
|
|
47
63
|
|
|
48
|
-
|
|
64
|
+
1. **Create campaigns** in the [Bliptar Dashboard](https://app.bliptar.com)
|
|
65
|
+
2. **Configure triggers** - when should the form appear?
|
|
66
|
+
- Page View - when user visits a page
|
|
67
|
+
- Scroll Depth - when user scrolls to X%
|
|
68
|
+
- Time on Page - after X seconds
|
|
69
|
+
- Exit Intent - when user is about to leave
|
|
70
|
+
- Custom Event - triggered via `bliptar.track()`
|
|
71
|
+
3. **Add the SDK** to your app with your API key
|
|
72
|
+
4. **Done** - forms appear automatically based on your campaign rules
|
|
49
73
|
|
|
50
|
-
|
|
51
|
-
import { createBliptar } from '@bliptarjs/sdk';
|
|
74
|
+
## Configuration
|
|
52
75
|
|
|
76
|
+
```javascript
|
|
53
77
|
const bliptar = createBliptar({
|
|
54
|
-
apiKey: 'your-api-key',
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
78
|
+
apiKey: 'your-api-key', // Required
|
|
79
|
+
debug: false, // Enable console logging
|
|
80
|
+
userId: 'user-123', // Pre-set user ID
|
|
81
|
+
userAttributes: { // User attributes for targeting
|
|
82
|
+
plan: 'pro',
|
|
83
|
+
company: 'Acme',
|
|
84
|
+
},
|
|
85
|
+
onShow: (form) => {}, // Callback when form appears
|
|
86
|
+
onSubmit: (answers) => {}, // Callback when form is submitted
|
|
87
|
+
onDismiss: () => {}, // Callback when form is dismissed
|
|
88
|
+
onError: (error) => {}, // Callback on errors
|
|
63
89
|
});
|
|
64
90
|
```
|
|
65
91
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
| Option | Type | Default | Description |
|
|
69
|
-
|--------|------|---------|-------------|
|
|
70
|
-
| `apiKey` | `string` | **required** | Your Bliptar API key |
|
|
71
|
-
| `apiUrl` | `string` | `https://api.bliptar.com` | API endpoint URL |
|
|
72
|
-
| `debug` | `boolean` | `false` | Enable console logging for debugging |
|
|
73
|
-
| `userId` | `string` | - | Pre-set user ID for attribution |
|
|
74
|
-
| `userAttributes` | `object` | - | Pre-set user attributes for targeting |
|
|
75
|
-
| `onShow` | `function` | - | Callback when a form is displayed |
|
|
76
|
-
| `onSubmit` | `function` | - | Callback when a response is submitted |
|
|
77
|
-
| `onDismiss` | `function` | - | Callback when a form is dismissed |
|
|
78
|
-
| `onError` | `function` | - | Callback when an error occurs |
|
|
79
|
-
|
|
80
|
-
---
|
|
92
|
+
## API Reference
|
|
81
93
|
|
|
82
94
|
### `init()`
|
|
83
95
|
|
|
84
|
-
Initializes the SDK.
|
|
96
|
+
Initializes the SDK. Fetches campaigns and sets up automatic triggers.
|
|
85
97
|
|
|
86
98
|
```javascript
|
|
87
|
-
bliptar.init();
|
|
99
|
+
await bliptar.init();
|
|
88
100
|
```
|
|
89
101
|
|
|
90
|
-
> **Note:** Call this once after creating the instance, typically when your app loads.
|
|
91
|
-
|
|
92
|
-
---
|
|
93
|
-
|
|
94
102
|
### `identify(userId, attributes?)`
|
|
95
103
|
|
|
96
|
-
Associates feedback with a
|
|
104
|
+
Associates feedback with a user. Call when user logs in.
|
|
97
105
|
|
|
98
106
|
```javascript
|
|
99
|
-
// Basic identification
|
|
100
|
-
bliptar.identify('user-123');
|
|
101
|
-
|
|
102
|
-
// With attributes for targeting
|
|
103
107
|
bliptar.identify('user-123', {
|
|
104
108
|
email: 'user@example.com',
|
|
105
109
|
plan: 'pro',
|
|
106
|
-
company: 'Acme Inc',
|
|
107
|
-
signupDate: '2024-01-15',
|
|
108
110
|
});
|
|
109
111
|
```
|
|
110
112
|
|
|
111
|
-
User attributes can be used in campaign targeting rules (e.g., only show to "pro" plan users).
|
|
112
|
-
|
|
113
|
-
---
|
|
114
|
-
|
|
115
113
|
### `track(event, data?)`
|
|
116
114
|
|
|
117
|
-
|
|
115
|
+
Triggers a custom event. Use with "Custom Event" campaign trigger.
|
|
118
116
|
|
|
119
117
|
```javascript
|
|
120
|
-
// Simple event
|
|
121
|
-
bliptar.track('checkout_complete');
|
|
122
|
-
|
|
123
|
-
// With event data
|
|
124
118
|
bliptar.track('checkout_complete', {
|
|
125
|
-
orderId: 'order-
|
|
119
|
+
orderId: 'order-123',
|
|
126
120
|
total: 99.99,
|
|
127
|
-
items: 3,
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
// Feature usage
|
|
131
|
-
bliptar.track('feature_used', {
|
|
132
|
-
feature: 'export_pdf',
|
|
133
|
-
duration: 5000,
|
|
134
121
|
});
|
|
135
122
|
```
|
|
136
123
|
|
|
137
|
-
Configure campaigns in the dashboard to trigger forms based on these events.
|
|
138
|
-
|
|
139
|
-
---
|
|
140
|
-
|
|
141
|
-
### `trackClicks(options)`
|
|
142
|
-
|
|
143
|
-
Sets up click tracking on specific elements. When a user clicks a matching element, it fires a `button_click` event that can trigger campaigns.
|
|
144
|
-
|
|
145
|
-
```javascript
|
|
146
|
-
// Track clicks on a specific button
|
|
147
|
-
bliptar.trackClicks({
|
|
148
|
-
selector: '#upgrade-button',
|
|
149
|
-
name: 'upgrade-cta',
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
// Track clicks on all pricing buttons
|
|
153
|
-
bliptar.trackClicks({
|
|
154
|
-
selector: '.pricing-cta',
|
|
155
|
-
name: 'pricing-click',
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
// Track only the first click
|
|
159
|
-
bliptar.trackClicks({
|
|
160
|
-
selector: '.signup-button',
|
|
161
|
-
name: 'signup-click',
|
|
162
|
-
once: true,
|
|
163
|
-
});
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
#### Options
|
|
167
|
-
|
|
168
|
-
| Option | Type | Default | Description |
|
|
169
|
-
|--------|------|---------|-------------|
|
|
170
|
-
| `selector` | `string` | **required** | CSS selector for elements to track |
|
|
171
|
-
| `name` | `string` | `selector` | Friendly name for the click target |
|
|
172
|
-
| `once` | `boolean` | `false` | Only track the first click per session |
|
|
173
|
-
|
|
174
|
-
#### Event Data Sent
|
|
175
|
-
|
|
176
|
-
When a click is detected, the following data is sent:
|
|
177
|
-
|
|
178
|
-
```javascript
|
|
179
|
-
{
|
|
180
|
-
selector: '.pricing-cta',
|
|
181
|
-
name: 'pricing-click',
|
|
182
|
-
element_text: 'Get Started',
|
|
183
|
-
element_id: 'cta-btn',
|
|
184
|
-
element_class: 'pricing-cta btn-primary'
|
|
185
|
-
}
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
---
|
|
189
|
-
|
|
190
|
-
### `enableExitIntent(options?)`
|
|
191
|
-
|
|
192
|
-
Enables exit intent detection. Triggers an `exit_intent` event when a user appears to be leaving the page.
|
|
193
|
-
|
|
194
|
-
```javascript
|
|
195
|
-
// Basic usage (triggers once per session)
|
|
196
|
-
bliptar.enableExitIntent();
|
|
197
|
-
|
|
198
|
-
// Custom configuration
|
|
199
|
-
bliptar.enableExitIntent({
|
|
200
|
-
threshold: 50, // Pixels from top of viewport
|
|
201
|
-
cooldown: 10000, // Ms before can trigger again
|
|
202
|
-
once: true, // Only trigger once per session
|
|
203
|
-
});
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
#### Options
|
|
207
|
-
|
|
208
|
-
| Option | Type | Default | Description |
|
|
209
|
-
|--------|------|---------|-------------|
|
|
210
|
-
| `threshold` | `number` | `50` | Distance in pixels from top of viewport to trigger |
|
|
211
|
-
| `cooldown` | `number` | `10000` | Milliseconds before exit intent can trigger again |
|
|
212
|
-
| `once` | `boolean` | `true` | Only trigger once per session |
|
|
213
|
-
|
|
214
|
-
#### How It Works
|
|
215
|
-
|
|
216
|
-
- **Desktop:** Detects when the mouse cursor leaves the viewport from the top (user moving to close tab, click back, or navigate away)
|
|
217
|
-
- **Mobile:** Detects when the page visibility changes (user switching apps or tabs)
|
|
218
|
-
|
|
219
|
-
---
|
|
220
|
-
|
|
221
124
|
### `showForm(formId)`
|
|
222
125
|
|
|
223
|
-
Manually
|
|
126
|
+
Manually show a specific form, bypassing campaign rules.
|
|
224
127
|
|
|
225
128
|
```javascript
|
|
226
|
-
// Show a form programmatically
|
|
227
129
|
await bliptar.showForm('form_abc123');
|
|
228
|
-
|
|
229
|
-
// Example: Show after completing onboarding
|
|
230
|
-
function completeOnboarding() {
|
|
231
|
-
// ... onboarding logic
|
|
232
|
-
bliptar.showForm('form_onboarding_feedback');
|
|
233
|
-
}
|
|
234
130
|
```
|
|
235
131
|
|
|
236
|
-
---
|
|
237
|
-
|
|
238
132
|
### `destroy()`
|
|
239
133
|
|
|
240
|
-
|
|
134
|
+
Cleanup - removes listeners and timers. Call when unmounting.
|
|
241
135
|
|
|
242
136
|
```javascript
|
|
243
137
|
bliptar.destroy();
|
|
244
138
|
```
|
|
245
139
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
## Usage Examples
|
|
249
|
-
|
|
250
|
-
### Basic Setup
|
|
251
|
-
|
|
252
|
-
```javascript
|
|
253
|
-
import { createBliptar } from '@bliptarjs/sdk';
|
|
254
|
-
|
|
255
|
-
const bliptar = createBliptar({
|
|
256
|
-
apiKey: 'blip_live_xxxxxxxx',
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
bliptar.init();
|
|
260
|
-
```
|
|
261
|
-
|
|
262
|
-
### With User Identification
|
|
263
|
-
|
|
264
|
-
```javascript
|
|
265
|
-
import { createBliptar } from '@bliptarjs/sdk';
|
|
266
|
-
|
|
267
|
-
const bliptar = createBliptar({
|
|
268
|
-
apiKey: 'blip_live_xxxxxxxx',
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
bliptar.init();
|
|
272
|
-
|
|
273
|
-
// When user logs in
|
|
274
|
-
function onUserLogin(user) {
|
|
275
|
-
bliptar.identify(user.id, {
|
|
276
|
-
email: user.email,
|
|
277
|
-
plan: user.subscription.plan,
|
|
278
|
-
createdAt: user.createdAt,
|
|
279
|
-
});
|
|
280
|
-
}
|
|
281
|
-
```
|
|
282
|
-
|
|
283
|
-
### E-commerce Checkout Feedback
|
|
284
|
-
|
|
285
|
-
```javascript
|
|
286
|
-
import { createBliptar } from '@bliptarjs/sdk';
|
|
287
|
-
|
|
288
|
-
const bliptar = createBliptar({
|
|
289
|
-
apiKey: 'blip_live_xxxxxxxx',
|
|
290
|
-
onSubmit: (answers) => {
|
|
291
|
-
// Send to your analytics
|
|
292
|
-
analytics.track('feedback_submitted', answers);
|
|
293
|
-
},
|
|
294
|
-
});
|
|
295
|
-
|
|
296
|
-
bliptar.init();
|
|
297
|
-
|
|
298
|
-
// Track successful purchase
|
|
299
|
-
function onPurchaseComplete(order) {
|
|
300
|
-
bliptar.track('purchase_complete', {
|
|
301
|
-
orderId: order.id,
|
|
302
|
-
total: order.total,
|
|
303
|
-
itemCount: order.items.length,
|
|
304
|
-
});
|
|
305
|
-
}
|
|
306
|
-
```
|
|
307
|
-
|
|
308
|
-
### Exit Intent Survey
|
|
309
|
-
|
|
310
|
-
```javascript
|
|
311
|
-
import { createBliptar } from '@bliptarjs/sdk';
|
|
312
|
-
|
|
313
|
-
const bliptar = createBliptar({
|
|
314
|
-
apiKey: 'blip_live_xxxxxxxx',
|
|
315
|
-
});
|
|
316
|
-
|
|
317
|
-
bliptar.init();
|
|
318
|
-
|
|
319
|
-
// Enable exit intent on pricing page
|
|
320
|
-
if (window.location.pathname === '/pricing') {
|
|
321
|
-
bliptar.enableExitIntent({
|
|
322
|
-
once: true, // Only show once
|
|
323
|
-
});
|
|
324
|
-
}
|
|
325
|
-
```
|
|
326
|
-
|
|
327
|
-
### Button Click Feedback
|
|
140
|
+
## Framework Examples
|
|
328
141
|
|
|
329
|
-
|
|
330
|
-
import { createBliptar } from '@bliptarjs/sdk';
|
|
331
|
-
|
|
332
|
-
const bliptar = createBliptar({
|
|
333
|
-
apiKey: 'blip_live_xxxxxxxx',
|
|
334
|
-
});
|
|
335
|
-
|
|
336
|
-
bliptar.init();
|
|
337
|
-
|
|
338
|
-
// Track clicks on upgrade buttons
|
|
339
|
-
bliptar.trackClicks({
|
|
340
|
-
selector: '[data-track="upgrade"]',
|
|
341
|
-
name: 'upgrade-button',
|
|
342
|
-
});
|
|
343
|
-
|
|
344
|
-
// Track clicks on help buttons
|
|
345
|
-
bliptar.trackClicks({
|
|
346
|
-
selector: '.help-button',
|
|
347
|
-
name: 'help-click',
|
|
348
|
-
});
|
|
349
|
-
```
|
|
350
|
-
|
|
351
|
-
### React Integration
|
|
142
|
+
### React
|
|
352
143
|
|
|
353
144
|
```jsx
|
|
354
145
|
import { useEffect } from 'react';
|
|
355
146
|
import { createBliptar } from '@bliptarjs/sdk';
|
|
356
147
|
|
|
357
148
|
const bliptar = createBliptar({
|
|
358
|
-
apiKey:
|
|
149
|
+
apiKey: process.env.REACT_APP_BLIPTAR_KEY,
|
|
359
150
|
});
|
|
360
151
|
|
|
361
152
|
function App() {
|
|
362
153
|
useEffect(() => {
|
|
363
154
|
bliptar.init();
|
|
364
|
-
|
|
365
|
-
return () => {
|
|
366
|
-
bliptar.destroy();
|
|
367
|
-
};
|
|
155
|
+
return () => bliptar.destroy();
|
|
368
156
|
}, []);
|
|
369
157
|
|
|
370
158
|
return <div>Your app</div>;
|
|
371
159
|
}
|
|
372
|
-
|
|
373
|
-
// In a component that needs to identify users
|
|
374
|
-
function UserProfile({ user }) {
|
|
375
|
-
useEffect(() => {
|
|
376
|
-
if (user) {
|
|
377
|
-
bliptar.identify(user.id, {
|
|
378
|
-
email: user.email,
|
|
379
|
-
plan: user.plan,
|
|
380
|
-
});
|
|
381
|
-
}
|
|
382
|
-
}, [user]);
|
|
383
|
-
|
|
384
|
-
return <div>Welcome, {user.name}</div>;
|
|
385
|
-
}
|
|
386
160
|
```
|
|
387
161
|
|
|
388
|
-
### Next.js
|
|
162
|
+
### Next.js
|
|
389
163
|
|
|
390
164
|
```jsx
|
|
391
|
-
//
|
|
165
|
+
// app/providers.js
|
|
166
|
+
'use client';
|
|
167
|
+
import { useEffect } from 'react';
|
|
392
168
|
import { createBliptar } from '@bliptarjs/sdk';
|
|
393
169
|
|
|
394
|
-
|
|
395
|
-
? createBliptar({ apiKey: process.env.
|
|
170
|
+
const bliptar = typeof window !== 'undefined'
|
|
171
|
+
? createBliptar({ apiKey: process.env.NEXT_PUBLIC_BLIPTAR_KEY })
|
|
396
172
|
: null;
|
|
397
173
|
|
|
398
|
-
|
|
399
|
-
'use client';
|
|
400
|
-
|
|
401
|
-
import { useEffect } from 'react';
|
|
402
|
-
import { bliptar } from '@/lib/bliptar';
|
|
403
|
-
|
|
404
|
-
export default function RootLayout({ children }) {
|
|
174
|
+
export function BliptarProvider({ children }) {
|
|
405
175
|
useEffect(() => {
|
|
406
176
|
bliptar?.init();
|
|
407
|
-
|
|
408
|
-
return () => {
|
|
409
|
-
bliptar?.destroy();
|
|
410
|
-
};
|
|
177
|
+
return () => bliptar?.destroy();
|
|
411
178
|
}, []);
|
|
412
179
|
|
|
180
|
+
return children;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// app/layout.js
|
|
184
|
+
import { BliptarProvider } from './providers';
|
|
185
|
+
|
|
186
|
+
export default function RootLayout({ children }) {
|
|
413
187
|
return (
|
|
414
188
|
<html>
|
|
415
|
-
<body>
|
|
189
|
+
<body>
|
|
190
|
+
<BliptarProvider>{children}</BliptarProvider>
|
|
191
|
+
</body>
|
|
416
192
|
</html>
|
|
417
193
|
);
|
|
418
194
|
}
|
|
419
195
|
```
|
|
420
196
|
|
|
421
|
-
### Vue
|
|
197
|
+
### Vue
|
|
422
198
|
|
|
423
199
|
```vue
|
|
424
200
|
<script setup>
|
|
@@ -426,95 +202,78 @@ import { onMounted, onUnmounted } from 'vue';
|
|
|
426
202
|
import { createBliptar } from '@bliptarjs/sdk';
|
|
427
203
|
|
|
428
204
|
const bliptar = createBliptar({
|
|
429
|
-
apiKey:
|
|
205
|
+
apiKey: import.meta.env.VITE_BLIPTAR_KEY,
|
|
430
206
|
});
|
|
431
207
|
|
|
432
|
-
onMounted(() =>
|
|
433
|
-
|
|
434
|
-
});
|
|
435
|
-
|
|
436
|
-
onUnmounted(() => {
|
|
437
|
-
bliptar.destroy();
|
|
438
|
-
});
|
|
208
|
+
onMounted(() => bliptar.init());
|
|
209
|
+
onUnmounted(() => bliptar.destroy());
|
|
439
210
|
</script>
|
|
440
211
|
```
|
|
441
212
|
|
|
442
|
-
|
|
213
|
+
## User Identification
|
|
443
214
|
|
|
444
|
-
|
|
215
|
+
Track feedback by user:
|
|
445
216
|
|
|
446
|
-
|
|
217
|
+
```javascript
|
|
218
|
+
// When user logs in
|
|
219
|
+
bliptar.identify(user.id, {
|
|
220
|
+
email: user.email,
|
|
221
|
+
plan: user.subscription.plan,
|
|
222
|
+
signupDate: user.createdAt,
|
|
223
|
+
});
|
|
224
|
+
```
|
|
447
225
|
|
|
448
|
-
|
|
449
|
-
|------|-------------|
|
|
450
|
-
| `page_view` | Fired on `init()` |
|
|
451
|
-
| `scroll_depth` | Percentage scrolled (0-100%) |
|
|
452
|
-
| `time_on_page` | Seconds spent on page |
|
|
453
|
-
| `device_type` | `desktop`, `mobile`, or `tablet` |
|
|
454
|
-
| `page_url` | Current page URL |
|
|
455
|
-
| `referrer` | Previous page URL |
|
|
226
|
+
User attributes can be used in campaign targeting rules.
|
|
456
227
|
|
|
457
|
-
|
|
228
|
+
## Advanced: Manual Triggers
|
|
458
229
|
|
|
459
|
-
|
|
230
|
+
For advanced use cases, you can manually set up triggers:
|
|
460
231
|
|
|
461
|
-
|
|
232
|
+
### `trackClicks(options)`
|
|
462
233
|
|
|
463
|
-
|
|
234
|
+
Track clicks on specific elements:
|
|
464
235
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
| **Text Input** | Free-form text responses |
|
|
236
|
+
```javascript
|
|
237
|
+
bliptar.trackClicks({
|
|
238
|
+
selector: '.upgrade-button',
|
|
239
|
+
name: 'upgrade-click',
|
|
240
|
+
once: true, // Only track first click
|
|
241
|
+
});
|
|
242
|
+
```
|
|
473
243
|
|
|
474
|
-
|
|
244
|
+
### `enableExitIntent(options?)`
|
|
245
|
+
|
|
246
|
+
Manually enable exit intent detection:
|
|
475
247
|
|
|
476
|
-
|
|
248
|
+
```javascript
|
|
249
|
+
bliptar.enableExitIntent({
|
|
250
|
+
threshold: 50, // Pixels from top
|
|
251
|
+
cooldown: 10000, // Ms before can trigger again
|
|
252
|
+
once: true, // Only trigger once per session
|
|
253
|
+
});
|
|
254
|
+
```
|
|
477
255
|
|
|
478
|
-
## TypeScript
|
|
256
|
+
## TypeScript
|
|
479
257
|
|
|
480
|
-
|
|
258
|
+
Full TypeScript support included:
|
|
481
259
|
|
|
482
260
|
```typescript
|
|
483
|
-
import {
|
|
484
|
-
createBliptar,
|
|
485
|
-
BliptarInstance,
|
|
486
|
-
BliptarConfig,
|
|
487
|
-
ClickTrackingOptions,
|
|
488
|
-
ExitIntentOptions,
|
|
489
|
-
} from '@bliptarjs/sdk';
|
|
261
|
+
import { createBliptar, BliptarConfig } from '@bliptarjs/sdk';
|
|
490
262
|
|
|
491
263
|
const config: BliptarConfig = {
|
|
492
|
-
apiKey: '
|
|
264
|
+
apiKey: 'your-api-key',
|
|
493
265
|
debug: true,
|
|
494
|
-
onSubmit: (answers) => {
|
|
495
|
-
console.log(answers);
|
|
496
|
-
},
|
|
497
266
|
};
|
|
498
267
|
|
|
499
|
-
const bliptar
|
|
268
|
+
const bliptar = createBliptar(config);
|
|
269
|
+
```
|
|
500
270
|
|
|
501
|
-
|
|
502
|
-
const clickOptions: ClickTrackingOptions = {
|
|
503
|
-
selector: '.cta-button',
|
|
504
|
-
name: 'main-cta',
|
|
505
|
-
once: true,
|
|
506
|
-
};
|
|
507
|
-
bliptar.trackClicks(clickOptions);
|
|
271
|
+
## Bundle Size
|
|
508
272
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
threshold: 50,
|
|
512
|
-
once: true,
|
|
513
|
-
};
|
|
514
|
-
bliptar.enableExitIntent(exitOptions);
|
|
515
|
-
```
|
|
273
|
+
- **ESM:** ~26KB (minified)
|
|
274
|
+
- **Gzipped:** ~8KB
|
|
516
275
|
|
|
517
|
-
|
|
276
|
+
Zero dependencies.
|
|
518
277
|
|
|
519
278
|
## Browser Support
|
|
520
279
|
|
|
@@ -525,44 +284,22 @@ bliptar.enableExitIntent(exitOptions);
|
|
|
525
284
|
| Safari | Latest 2 versions |
|
|
526
285
|
| Edge | Latest 2 versions |
|
|
527
286
|
|
|
528
|
-
---
|
|
529
|
-
|
|
530
|
-
## Bundle Size
|
|
531
|
-
|
|
532
|
-
- **ESM:** ~25KB (minified)
|
|
533
|
-
- **Gzipped:** ~7KB
|
|
534
|
-
|
|
535
|
-
The SDK has zero dependencies.
|
|
536
|
-
|
|
537
|
-
---
|
|
538
|
-
|
|
539
287
|
## Troubleshooting
|
|
540
288
|
|
|
541
289
|
### Forms not showing?
|
|
542
290
|
|
|
543
|
-
1. **Check
|
|
544
|
-
2. **Check
|
|
545
|
-
3. **Enable debug mode** -
|
|
546
|
-
4. **Check throttling** -
|
|
547
|
-
|
|
548
|
-
### Debug Mode
|
|
291
|
+
1. **Check API key** - ensure it's valid
|
|
292
|
+
2. **Check campaigns** - verify campaign is active in dashboard
|
|
293
|
+
3. **Enable debug mode** - set `debug: true` to see logs
|
|
294
|
+
4. **Check throttling** - campaigns have frequency limits
|
|
549
295
|
|
|
550
296
|
```javascript
|
|
551
297
|
const bliptar = createBliptar({
|
|
552
|
-
apiKey: '
|
|
553
|
-
debug: true, //
|
|
298
|
+
apiKey: 'your-key',
|
|
299
|
+
debug: true, // See what's happening
|
|
554
300
|
});
|
|
555
301
|
```
|
|
556
302
|
|
|
557
|
-
This will log:
|
|
558
|
-
- SDK initialization
|
|
559
|
-
- Events being tracked
|
|
560
|
-
- Form display triggers
|
|
561
|
-
- API responses
|
|
562
|
-
- Errors
|
|
563
|
-
|
|
564
|
-
---
|
|
565
|
-
|
|
566
303
|
## License
|
|
567
304
|
|
|
568
305
|
MIT
|
package/dist/index.cjs
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
"use strict";var
|
|
2
|
-
--bliptar-bg: ${
|
|
3
|
-
--bliptar-surface: ${
|
|
4
|
-
--bliptar-text: ${
|
|
5
|
-
--bliptar-text-secondary: ${
|
|
6
|
-
--bliptar-primary: ${
|
|
7
|
-
--bliptar-primary-hover: ${
|
|
8
|
-
--bliptar-border: ${
|
|
9
|
-
--bliptar-shadow: ${
|
|
10
|
-
--bliptar-star: ${
|
|
11
|
-
`}var
|
|
12
|
-
:root { ${
|
|
1
|
+
"use strict";var x=Object.defineProperty;var I=Object.getOwnPropertyDescriptor;var L=Object.getOwnPropertyNames;var P=Object.prototype.hasOwnProperty;var D=(o,e)=>{for(var t in e)x(o,t,{get:e[t],enumerable:!0})},A=(o,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of L(e))!P.call(o,i)&&i!==t&&x(o,i,{get:()=>e[i],enumerable:!(r=I(e,i))||r.enumerable});return o};var B=o=>A(x({},"__esModule",{value:!0}),o);var F={};D(F,{Bliptar:()=>h,createBliptar:()=>k});module.exports=B(F);var b={mode:"light",backgroundColor:"#ffffff",surfaceColor:"#f5f5f5",textPrimary:"#1a1a1a",textSecondary:"#666666",primaryColor:"#3b82f6",primaryHover:"#2563eb",borderColor:"#e0e0e0",shadowColor:"rgba(0, 0, 0, 0.15)",starColor:"#fbbf24"},u={mode:"dark",backgroundColor:"#1a1a1a",surfaceColor:"#2a2a2a",textPrimary:"#ffffff",textSecondary:"#a0a0a0",primaryColor:"#3b82f6",primaryHover:"#60a5fa",borderColor:"#333333",shadowColor:"rgba(0, 0, 0, 0.4)",starColor:"#fbbf24"};function j(o,e){try{let r=window.getComputedStyle(o).getPropertyValue(e);return r&&r!=="rgba(0, 0, 0, 0)"?r:null}catch{return null}}function H(o){try{return getComputedStyle(document.documentElement).getPropertyValue(o).trim()||null}catch{return null}}function C(o){let e=o.match(/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i);if(e)return{r:parseInt(e[1],16),g:parseInt(e[2],16),b:parseInt(e[3],16)};let t=o.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);return t?{r:parseInt(t[1],10),g:parseInt(t[2],10),b:parseInt(t[3],10)}:null}function M(o,e,t){let[r,i,a]=[o,e,t].map(n=>(n=n/255,n<=.03928?n/12.92:Math.pow((n+.055)/1.055,2.4)));return .2126*r+.7152*i+.0722*a}function _(o){let e=C(o);return e?M(e.r,e.g,e.b)<.5:!1}function g(o,e){let t=C(o);if(!t)return o;let r=i=>Math.min(255,Math.max(0,Math.round(i+e/100*255)));return`rgb(${r(t.r)}, ${r(t.g)}, ${r(t.b)})`}function z(o){return _(o)?"#ffffff":"#1a1a1a"}function R(o,e){let t={},r=null;o&&(r=document.querySelector(o)),r||(r=document.body);let i=j(r,"background-color");if(i){let a=_(i);t.mode=a?"dark":"light",t.backgroundColor=i,t.surfaceColor=g(i,a?10:-5),t.textPrimary=z(i),t.textSecondary=g(t.textPrimary,a?-30:30),t.borderColor=g(i,a?20:-15),t.shadowColor=a?"rgba(0, 0, 0, 0.4)":"rgba(0, 0, 0, 0.15)"}if(e){let a={"--primary":"primaryColor","--primary-color":"primaryColor","--accent":"primaryColor","--accent-color":"primaryColor","--brand":"primaryColor","--brand-color":"primaryColor","--color-primary":"primaryColor","--background":"backgroundColor","--bg":"backgroundColor","--bg-color":"backgroundColor","--text":"textPrimary","--text-color":"textPrimary","--foreground":"textPrimary","--border":"borderColor","--border-color":"borderColor"};for(let[n,l]of Object.entries(a)){let c=H(n);c&&l!=="mode"&&(t[l]=c)}}return t.primaryColor&&(t.primaryHover=g(t.primaryColor,t.mode==="dark"?15:-10)),Object.keys(t).length>0?t:null}function w(o){let{theme:e,customTheme:t,adaptiveOptions:r}=o;if(e==="custom"&&t)return{mode:_(t.backgroundColor)?"dark":"light",backgroundColor:t.backgroundColor,surfaceColor:t.surfaceColor,textPrimary:t.textPrimary,textSecondary:t.textSecondary,primaryColor:t.primaryColor,primaryHover:t.primaryHover,borderColor:t.borderColor,shadowColor:t.shadowColor,starColor:"#fbbf24"};if(e==="adaptive"){let i=r||{fallback:"light",autoContrast:!0},a=R(i.sourceElement,i.inheritCssVars);return a?{...a.mode==="dark"?u:b,...a}:i.fallback==="dark"?u:b}return e==="auto"?window.matchMedia("(prefers-color-scheme: dark)").matches?u:b:e==="dark"?u:b}function T(o){return`
|
|
2
|
+
--bliptar-bg: ${o.backgroundColor};
|
|
3
|
+
--bliptar-surface: ${o.surfaceColor};
|
|
4
|
+
--bliptar-text: ${o.textPrimary};
|
|
5
|
+
--bliptar-text-secondary: ${o.textSecondary};
|
|
6
|
+
--bliptar-primary: ${o.primaryColor};
|
|
7
|
+
--bliptar-primary-hover: ${o.primaryHover};
|
|
8
|
+
--bliptar-border: ${o.borderColor};
|
|
9
|
+
--bliptar-shadow: ${o.shadowColor};
|
|
10
|
+
--bliptar-star: ${o.starColor};
|
|
11
|
+
`}var f=class{constructor(e,t){this.container=null;this.currentIndex=0;this.answers={};this.form=e,this.options=t,this.theme=w(e.appearance)}render(){this.createContainer(),this.injectStyles(),this.renderQuestion()}destroy(){if(this.container){let e=this.container.querySelector(".bliptar__card");e?(e.classList.add("bliptar__card--exit"),setTimeout(()=>{this.container?.remove(),this.container=null},200)):(this.container.remove(),this.container=null)}}createContainer(){this.container=document.createElement("div"),this.container.id="bliptar-form",this.container.className=`bliptar bliptar--${this.form.appearance.position}`,document.body.appendChild(this.container)}injectStyles(){let e=document.getElementById("bliptar-styles");e&&e.remove();let t=document.createElement("style");t.id="bliptar-styles",t.textContent=this.getStyles(),document.head.appendChild(t)}getStyles(){let{appearance:e}=this.form,t=this.theme,r=e.animation||"slide-up";return`
|
|
12
|
+
:root { ${T(t)} }
|
|
13
13
|
|
|
14
14
|
.bliptar {
|
|
15
15
|
position: fixed;
|
|
@@ -372,20 +372,20 @@
|
|
|
372
372
|
}
|
|
373
373
|
|
|
374
374
|
@media print { .bliptar { display: none !important; } }
|
|
375
|
-
`}getAnimationStyles(e){switch(e){case"slide-up":return"animation: bliptar-slide-up 0.35s cubic-bezier(0.16, 1, 0.3, 1);";case"fade":return"animation: bliptar-fade 0.3s ease-out;";case"scale":return"animation: bliptar-scale 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);";case"none":return"";default:return"animation: bliptar-slide-up 0.35s cubic-bezier(0.16, 1, 0.3, 1);"}}renderQuestion(){if(!this.container)return;let{questions:e,appearance:t,behavior:r}=this.form,
|
|
375
|
+
`}getAnimationStyles(e){switch(e){case"slide-up":return"animation: bliptar-slide-up 0.35s cubic-bezier(0.16, 1, 0.3, 1);";case"fade":return"animation: bliptar-fade 0.3s ease-out;";case"scale":return"animation: bliptar-scale 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);";case"none":return"";default:return"animation: bliptar-slide-up 0.35s cubic-bezier(0.16, 1, 0.3, 1);"}}renderQuestion(){if(!this.container)return;let{questions:e,appearance:t,behavior:r}=this.form,i=e[this.currentIndex],a=this.currentIndex===e.length-1,n="";if(t.backdrop&&(n+='<div class="bliptar__backdrop"></div>'),n+='<div class="bliptar__card">',r.allowDismiss&&(n+=`<button class="bliptar__close" aria-label="Close">
|
|
376
376
|
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
|
|
377
377
|
<path d="M1 1l12 12M1 13L13 1"/>
|
|
378
378
|
</svg>
|
|
379
|
-
</button>`),r.showProgress&&e.length>1){n+='<div class="bliptar__progress">';for(let l=0;l<e.length;l++)n+=`<div class="bliptar__progress-bar${l<=this.currentIndex?" bliptar__progress-bar--active":""}"></div>`;n+="</div>"}n+=`<div class="bliptar__question">${this.escapeHtml(
|
|
379
|
+
</button>`),r.showProgress&&e.length>1){n+='<div class="bliptar__progress">';for(let l=0;l<e.length;l++)n+=`<div class="bliptar__progress-bar${l<=this.currentIndex?" bliptar__progress-bar--active":""}"></div>`;n+="</div>"}n+=`<div class="bliptar__question">${this.escapeHtml(i.question)}</div>`,n+=this.renderAnswerOptions(i),(!r.autoAdvance||i.type==="text-input")&&(n+='<div class="bliptar__nav">',n+=this.currentIndex>0?'<button class="bliptar__nav-btn bliptar__nav-btn--back">Back</button>':"<div></div>",n+=`<button class="bliptar__nav-btn bliptar__nav-btn--next">${a?"Submit":"Continue"}</button>`,n+="</div>"),n+="</div>",this.container.innerHTML=n,this.attachEventListeners()}escapeHtml(e){let t=document.createElement("div");return t.textContent=e,t.innerHTML}renderAnswerOptions(e){let t=`q${this.currentIndex}`,r=this.answers[t];switch(e.type){case"binary":return`<div class="bliptar__options">
|
|
380
380
|
<button class="bliptar__btn${r===!0?" bliptar__btn--selected":""}" data-value="true">
|
|
381
381
|
${e.style==="thumbs"?"\u{1F44D} ":""}${this.escapeHtml(e.positiveLabel)}
|
|
382
382
|
</button>
|
|
383
383
|
<button class="bliptar__btn${r===!1?" bliptar__btn--selected":""}" data-value="false">
|
|
384
384
|
${e.style==="thumbs"?"\u{1F44E} ":""}${this.escapeHtml(e.negativeLabel)}
|
|
385
385
|
</button>
|
|
386
|
-
</div>`;case"star-rating":let
|
|
386
|
+
</div>`;case"star-rating":let i='<div class="bliptar__stars">';for(let s=1;s<=e.maxStars;s++)i+=`<span class="bliptar__star${r>=s?" bliptar__star--active":""}" data-value="${s}">\u2605</span>`;return i+"</div>";case"emoji-scale":let a=["\u{1F61E}","\u{1F615}","\u{1F610}","\u{1F642}","\u{1F60A}"],n=["\u{1F61E}","\u{1F610}","\u{1F60A}"],l=e.scale===3?n:a,c='<div class="bliptar__emojis">';return l.forEach((s,d)=>{c+=`<span class="bliptar__emoji${r===d+1?" bliptar__emoji--active":""}" data-value="${d+1}" title="${e.labels?.[d]||""}">${s}</span>`}),c+"</div>";case"nps":let p='<div class="bliptar__nps">';for(let s=0;s<=10;s++)p+=`<button class="bliptar__nps-btn${r===s?" bliptar__nps-btn--selected":""}" data-value="${s}">${s}</button>`;return p+=`</div><div class="bliptar__nps-labels"><span>${this.escapeHtml(e.lowLabel)}</span><span>${this.escapeHtml(e.highLabel)}</span></div>`,p;case"single-choice":let m='<div class="bliptar__options">';return e.options.forEach(s=>{m+=`<button class="bliptar__btn${r===s?" bliptar__btn--selected":""}" data-value="${this.escapeHtml(s)}">${this.escapeHtml(s)}</button>`}),m+"</div>";case"text-input":return`<textarea class="bliptar__textarea" placeholder="${this.escapeHtml(e.placeholder)}" maxlength="${e.maxLength}">${r||""}</textarea>`;default:return""}}attachEventListeners(){if(!this.container)return;let e=this.form.questions[this.currentIndex],{behavior:t}=this.form,r=this.container.querySelector(".bliptar__close");r&&r.addEventListener("click",()=>this.options.onDismiss());let i=this.container.querySelector(".bliptar__backdrop");i&&t.allowDismiss&&i.addEventListener("click",()=>this.options.onDismiss());let a=s=>{this.answers[`q${this.currentIndex}`]=s,t.autoAdvance&&e.type!=="text-input"?setTimeout(()=>this.goNext(),t.autoAdvanceDelay||300):this.renderQuestion()};this.container.querySelectorAll(".bliptar__btn, .bliptar__nps-btn").forEach(s=>{s.addEventListener("click",()=>{let d=s.dataset.value;e.type==="binary"?a(d==="true"):e.type==="nps"?a(parseInt(d,10)):a(d)})});let n=this.container.querySelectorAll(".bliptar__star");n.forEach((s,d)=>{s.addEventListener("mouseenter",()=>{n.forEach((v,E)=>v.classList.toggle("bliptar__star--active",E<=d))}),s.addEventListener("click",()=>a(parseInt(s.dataset.value,10)))});let l=this.container.querySelector(".bliptar__stars");l&&l.addEventListener("mouseleave",()=>{let s=this.answers[`q${this.currentIndex}`];n.forEach((d,v)=>d.classList.toggle("bliptar__star--active",s?v<s:!1))}),this.container.querySelectorAll(".bliptar__emoji").forEach(s=>{s.addEventListener("click",()=>a(parseInt(s.dataset.value,10)))});let c=this.container.querySelector(".bliptar__textarea");c&&(c.addEventListener("input",()=>{this.answers[`q${this.currentIndex}`]=c.value}),setTimeout(()=>c.focus(),100));let p=this.container.querySelector(".bliptar__nav-btn--back");p&&p.addEventListener("click",()=>this.goBack());let m=this.container.querySelector(".bliptar__nav-btn--next");m&&m.addEventListener("click",()=>this.goNext())}goBack(){this.currentIndex>0&&(this.currentIndex--,this.renderQuestion())}async goNext(){this.currentIndex===this.form.questions.length-1?await this.submit():(this.currentIndex++,this.renderQuestion())}async submit(){if(this.container){let e=this.container.querySelector(".bliptar__card");e&&(e.innerHTML='<div class="bliptar__loading"><div class="bliptar__spinner"></div></div>')}try{await this.options.onSubmit(this.answers),this.form.behavior.showThankYou?(this.renderThankYou(),setTimeout(()=>this.options.onDismiss(),this.form.behavior.thankYouDuration||2e3)):this.options.onDismiss()}catch{this.options.onDismiss()}}renderThankYou(){if(!this.container)return;let{appearance:e,behavior:t}=this.form,r="";e.backdrop&&(r+='<div class="bliptar__backdrop"></div>'),r+=`<div class="bliptar__card">
|
|
387
387
|
<div class="bliptar__thankyou">
|
|
388
388
|
<div class="bliptar__thankyou-emoji">\u{1F389}</div>
|
|
389
389
|
<div class="bliptar__thankyou-text">${this.escapeHtml(t.thankYouMessage||"Thank you for your feedback!")}</div>
|
|
390
390
|
</div>
|
|
391
|
-
</div>`,this.container.innerHTML=r}};function
|
|
391
|
+
</div>`,this.container.innerHTML=r}};function $(){let o="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",e="";for(let t=0;t<16;t++)e+=o.charAt(Math.floor(Math.random()*o.length));return`sess_${e}`}function y(){let o=navigator.userAgent;return/tablet|ipad|playbook|silk/i.test(o)?"tablet":/mobile|iphone|ipod|android|blackberry|opera mini|iemobile/i.test(o)?"mobile":"desktop"}function S(o,e){let t;return(...r)=>{clearTimeout(t),t=setTimeout(()=>o(...r),e)}}var O="https://api.bliptar.com",h=class{constructor(e){this.renderer=null;this.scrollDepth=0;this.maxScrollDepth=0;this.isInitialized=!1;this.eventListeners=[];this.trackedClicks=new Set;this.exitIntentTriggered=!1;this.exitIntentCooldownTimer=null;this.timeOnPageTimers=[];this.scrollDepthThresholds=new Set;this.triggeredScrollThresholds=new Set;this.campaigns=[];this.config={apiUrl:O,debug:!1,onShow:()=>{},onSubmit:()=>{},onDismiss:()=>{},onError:()=>{},...e},this.sessionId=this.loadOrCreateSessionId(),this.pageLoadTime=Date.now()}async init(){if(this.isInitialized)return;this.isInitialized=!0,this.log("Initializing Bliptar SDK"),await this.fetchConfigAndSetupTriggers();let e=S(()=>{let t=document.documentElement,r=window.scrollY||t.scrollTop,i=t.scrollHeight-t.clientHeight;this.scrollDepth=i>0?r/i:0;let a=Math.round(this.scrollDepth*100);a>this.maxScrollDepth&&(this.maxScrollDepth=a,this.checkScrollThresholds(a))},100);this.addEventListener(window,"scroll",e),this.track("page_view")}async fetchConfigAndSetupTriggers(){try{let e=await fetch(`${this.config.apiUrl}/api/sdk/config`,{headers:{Authorization:`Bearer ${this.config.apiKey}`}});if(!e.ok){this.log("Failed to fetch config:",e.status);return}let t=await e.json();this.campaigns=t.campaigns||[],this.log("Loaded campaigns:",this.campaigns.length),this.setupTriggersFromCampaigns()}catch(e){this.handleError(e)}}setupTriggersFromCampaigns(){let e=new Set;for(let t of this.campaigns){if(e.add(t.trigger_event),t.trigger_event==="scroll_depth"){let r=t.trigger_conditions?.find(i=>i.field==="scroll_depth_percent");r?.value&&this.scrollDepthThresholds.add(Number(r.value))}if(t.trigger_event==="time_on_page"){let r=t.trigger_conditions?.find(i=>i.field==="time_on_page_seconds");if(r?.value){let i=Number(r.value);this.setupTimeOnPageTrigger(i)}}}this.log("Active trigger types:",Array.from(e)),e.has("exit_intent")&&(this.log("Auto-enabling exit intent detection"),this.enableExitIntent({once:!0})),this.scrollDepthThresholds.size>0&&this.log("Scroll depth thresholds:",Array.from(this.scrollDepthThresholds))}setupTimeOnPageTrigger(e){this.log("Setting up time on page trigger:",e,"seconds");let t=setTimeout(()=>{this.track("time_on_page",{seconds:e,actual_time:Math.floor((Date.now()-this.pageLoadTime)/1e3)})},e*1e3);this.timeOnPageTimers.push(t)}checkScrollThresholds(e){for(let t of this.scrollDepthThresholds)e>=t&&!this.triggeredScrollThresholds.has(t)&&(this.triggeredScrollThresholds.add(t),this.log("Scroll threshold reached:",t+"%"),this.track("scroll_depth",{threshold:t,actual_depth:e}))}identify(e,t){this.config.userId=e,this.config.userAttributes={...this.config.userAttributes,...t},this.log("User identified:",e)}async track(e,t){this.log("Tracking event:",e,t);let r=this.getContext();try{let i=await fetch(`${this.config.apiUrl}/api/sdk/trigger`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.config.apiKey}`},body:JSON.stringify({session_id:this.sessionId,user_id:this.config.userId,event:e,event_data:t||{},context:r})});if(!i.ok)throw new Error(`API error: ${i.status}`);let a=await i.json();a.show_form&&a.form&&await this.displayForm(a.form,a.campaign_id)}catch(i){this.handleError(i)}}async showForm(e){this.log("Manually showing form:",e);try{let t=await fetch(`${this.config.apiUrl}/api/sdk/form/${e}`,{headers:{Authorization:`Bearer ${this.config.apiKey}`}});if(!t.ok)throw new Error(`Failed to fetch form: ${t.status}`);let r=await t.json();await this.displayForm(r)}catch(t){this.handleError(t)}}trackClicks(e){let{selector:t,name:r,once:i=!1}=e,a=r||t;this.log("Setting up click tracking for:",t);let n=l=>{let p=l.target.closest(t);p&&(i&&this.trackedClicks.has(a)||(this.log("Button click detected:",a),i&&this.trackedClicks.add(a),this.track("button_click",{selector:t,name:a,element_text:p.textContent?.trim().slice(0,100)||"",element_id:p.id||null,element_class:p.className||null})))};this.addEventListener(document,"click",n)}enableExitIntent(e={}){let{threshold:t=50,cooldown:r=1e4,once:i=!0}=e;this.log("Enabling exit intent detection");let a=l=>{l.clientY>t||i&&this.exitIntentTriggered||this.exitIntentCooldownTimer||(this.log("Exit intent detected"),this.exitIntentTriggered=!0,this.track("exit_intent",{trigger:"mouse_leave",mouse_y:l.clientY}),i||(this.exitIntentCooldownTimer=setTimeout(()=>{this.exitIntentCooldownTimer=null},r)))};this.addEventListener(document,"mouseleave",a);let n=()=>{if(document.visibilityState==="hidden"){if(i&&this.exitIntentTriggered)return;this.log("Exit intent detected (visibility change)"),this.exitIntentTriggered=!0,this.track("exit_intent",{trigger:"visibility_change"})}};y()!=="desktop"&&this.addEventListener(document,"visibilitychange",n)}destroy(){this.log("Destroying Bliptar SDK"),this.eventListeners.forEach(({target:e,type:t,handler:r})=>{e.removeEventListener(t,r)}),this.eventListeners=[],this.renderer&&(this.renderer.destroy(),this.renderer=null),this.exitIntentCooldownTimer&&(clearTimeout(this.exitIntentCooldownTimer),this.exitIntentCooldownTimer=null),this.timeOnPageTimers.forEach(e=>clearTimeout(e)),this.timeOnPageTimers=[],this.trackedClicks.clear(),this.exitIntentTriggered=!1,this.scrollDepthThresholds.clear(),this.triggeredScrollThresholds.clear(),this.campaigns=[],this.maxScrollDepth=0,this.isInitialized=!1}async displayForm(e,t){this.log("Displaying form:",e.formId),this.config.onShow?.(e),this.renderer=new f(e,{onSubmit:async r=>{await this.submitResponse(e.formId,r,t),this.config.onSubmit?.(r)},onDismiss:()=>{this.config.onDismiss?.(),this.renderer?.destroy(),this.renderer=null}}),this.renderer.render()}async submitResponse(e,t,r){this.log("Submitting response:",e,t);let i=this.getContext(),a=new Date().toISOString();try{await fetch(`${this.config.apiUrl}/api/sdk/submit`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.config.apiKey}`},body:JSON.stringify({session_id:this.sessionId,user_id:this.config.userId,form_id:e,campaign_id:r,answers:t,context:i,completed_at:a})})}catch(n){this.handleError(n)}}getContext(){return{pageUrl:window.location.href,referrer:document.referrer,timeOnPage:Math.floor((Date.now()-this.pageLoadTime)/1e3),scrollDepth:Math.round(this.scrollDepth*100)/100,deviceType:y()}}loadOrCreateSessionId(){let e="bliptar_session",t=sessionStorage.getItem(e);return t||(t=$(),sessionStorage.setItem(e,t)),t}addEventListener(e,t,r){e.addEventListener(t,r),this.eventListeners.push({target:e,type:t,handler:r})}log(...e){this.config.debug&&console.log("[Bliptar]",...e)}handleError(e){this.log("Error:",e),this.config.onError?.(e)}};function k(o){return new h(o)}if(typeof window<"u"){let o=document.currentScript;if(o?.dataset.apiKey){let e=k({apiKey:o.dataset.apiKey,debug:o.dataset.debug==="true"});window.bliptar=e,e.init()}}0&&(module.exports={Bliptar,createBliptar});
|
package/dist/index.d.cts
CHANGED
|
@@ -26,7 +26,7 @@ interface ExitIntentOptions {
|
|
|
26
26
|
once?: boolean;
|
|
27
27
|
}
|
|
28
28
|
interface BliptarInstance {
|
|
29
|
-
init: () => void
|
|
29
|
+
init: () => Promise<void>;
|
|
30
30
|
identify: (userId: string, attributes?: Record<string, unknown>) => void;
|
|
31
31
|
track: (event: string, data?: Record<string, unknown>) => void;
|
|
32
32
|
showForm: (formId: string) => Promise<void>;
|
|
@@ -128,13 +128,22 @@ declare class Bliptar implements BliptarInstance {
|
|
|
128
128
|
private renderer;
|
|
129
129
|
private pageLoadTime;
|
|
130
130
|
private scrollDepth;
|
|
131
|
+
private maxScrollDepth;
|
|
131
132
|
private isInitialized;
|
|
132
133
|
private eventListeners;
|
|
133
134
|
private trackedClicks;
|
|
134
135
|
private exitIntentTriggered;
|
|
135
136
|
private exitIntentCooldownTimer;
|
|
137
|
+
private timeOnPageTimers;
|
|
138
|
+
private scrollDepthThresholds;
|
|
139
|
+
private triggeredScrollThresholds;
|
|
140
|
+
private campaigns;
|
|
136
141
|
constructor(config: BliptarConfig);
|
|
137
|
-
init(): void
|
|
142
|
+
init(): Promise<void>;
|
|
143
|
+
private fetchConfigAndSetupTriggers;
|
|
144
|
+
private setupTriggersFromCampaigns;
|
|
145
|
+
private setupTimeOnPageTrigger;
|
|
146
|
+
private checkScrollThresholds;
|
|
138
147
|
identify(userId: string, attributes?: Record<string, unknown>): void;
|
|
139
148
|
track(event: string, data?: Record<string, unknown>): Promise<void>;
|
|
140
149
|
showForm(formId: string): Promise<void>;
|
package/dist/index.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ interface ExitIntentOptions {
|
|
|
26
26
|
once?: boolean;
|
|
27
27
|
}
|
|
28
28
|
interface BliptarInstance {
|
|
29
|
-
init: () => void
|
|
29
|
+
init: () => Promise<void>;
|
|
30
30
|
identify: (userId: string, attributes?: Record<string, unknown>) => void;
|
|
31
31
|
track: (event: string, data?: Record<string, unknown>) => void;
|
|
32
32
|
showForm: (formId: string) => Promise<void>;
|
|
@@ -128,13 +128,22 @@ declare class Bliptar implements BliptarInstance {
|
|
|
128
128
|
private renderer;
|
|
129
129
|
private pageLoadTime;
|
|
130
130
|
private scrollDepth;
|
|
131
|
+
private maxScrollDepth;
|
|
131
132
|
private isInitialized;
|
|
132
133
|
private eventListeners;
|
|
133
134
|
private trackedClicks;
|
|
134
135
|
private exitIntentTriggered;
|
|
135
136
|
private exitIntentCooldownTimer;
|
|
137
|
+
private timeOnPageTimers;
|
|
138
|
+
private scrollDepthThresholds;
|
|
139
|
+
private triggeredScrollThresholds;
|
|
140
|
+
private campaigns;
|
|
136
141
|
constructor(config: BliptarConfig);
|
|
137
|
-
init(): void
|
|
142
|
+
init(): Promise<void>;
|
|
143
|
+
private fetchConfigAndSetupTriggers;
|
|
144
|
+
private setupTriggersFromCampaigns;
|
|
145
|
+
private setupTimeOnPageTrigger;
|
|
146
|
+
private checkScrollThresholds;
|
|
138
147
|
identify(userId: string, attributes?: Record<string, unknown>): void;
|
|
139
148
|
track(event: string, data?: Record<string, unknown>): Promise<void>;
|
|
140
149
|
showForm(formId: string): Promise<void>;
|
package/dist/index.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
var
|
|
2
|
-
--bliptar-bg: ${
|
|
3
|
-
--bliptar-surface: ${
|
|
4
|
-
--bliptar-text: ${
|
|
5
|
-
--bliptar-text-secondary: ${
|
|
6
|
-
--bliptar-primary: ${
|
|
7
|
-
--bliptar-primary-hover: ${
|
|
8
|
-
--bliptar-border: ${
|
|
9
|
-
--bliptar-shadow: ${
|
|
10
|
-
--bliptar-star: ${
|
|
11
|
-
`}var
|
|
1
|
+
var b={mode:"light",backgroundColor:"#ffffff",surfaceColor:"#f5f5f5",textPrimary:"#1a1a1a",textSecondary:"#666666",primaryColor:"#3b82f6",primaryHover:"#2563eb",borderColor:"#e0e0e0",shadowColor:"rgba(0, 0, 0, 0.15)",starColor:"#fbbf24"},u={mode:"dark",backgroundColor:"#1a1a1a",surfaceColor:"#2a2a2a",textPrimary:"#ffffff",textSecondary:"#a0a0a0",primaryColor:"#3b82f6",primaryHover:"#60a5fa",borderColor:"#333333",shadowColor:"rgba(0, 0, 0, 0.4)",starColor:"#fbbf24"};function E(o,e){try{let r=window.getComputedStyle(o).getPropertyValue(e);return r&&r!=="rgba(0, 0, 0, 0)"?r:null}catch{return null}}function I(o){try{return getComputedStyle(document.documentElement).getPropertyValue(o).trim()||null}catch{return null}}function y(o){let e=o.match(/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i);if(e)return{r:parseInt(e[1],16),g:parseInt(e[2],16),b:parseInt(e[3],16)};let t=o.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);return t?{r:parseInt(t[1],10),g:parseInt(t[2],10),b:parseInt(t[3],10)}:null}function L(o,e,t){let[r,i,a]=[o,e,t].map(n=>(n=n/255,n<=.03928?n/12.92:Math.pow((n+.055)/1.055,2.4)));return .2126*r+.7152*i+.0722*a}function x(o){let e=y(o);return e?L(e.r,e.g,e.b)<.5:!1}function g(o,e){let t=y(o);if(!t)return o;let r=i=>Math.min(255,Math.max(0,Math.round(i+e/100*255)));return`rgb(${r(t.r)}, ${r(t.g)}, ${r(t.b)})`}function P(o){return x(o)?"#ffffff":"#1a1a1a"}function D(o,e){let t={},r=null;o&&(r=document.querySelector(o)),r||(r=document.body);let i=E(r,"background-color");if(i){let a=x(i);t.mode=a?"dark":"light",t.backgroundColor=i,t.surfaceColor=g(i,a?10:-5),t.textPrimary=P(i),t.textSecondary=g(t.textPrimary,a?-30:30),t.borderColor=g(i,a?20:-15),t.shadowColor=a?"rgba(0, 0, 0, 0.4)":"rgba(0, 0, 0, 0.15)"}if(e){let a={"--primary":"primaryColor","--primary-color":"primaryColor","--accent":"primaryColor","--accent-color":"primaryColor","--brand":"primaryColor","--brand-color":"primaryColor","--color-primary":"primaryColor","--background":"backgroundColor","--bg":"backgroundColor","--bg-color":"backgroundColor","--text":"textPrimary","--text-color":"textPrimary","--foreground":"textPrimary","--border":"borderColor","--border-color":"borderColor"};for(let[n,l]of Object.entries(a)){let c=I(n);c&&l!=="mode"&&(t[l]=c)}}return t.primaryColor&&(t.primaryHover=g(t.primaryColor,t.mode==="dark"?15:-10)),Object.keys(t).length>0?t:null}function k(o){let{theme:e,customTheme:t,adaptiveOptions:r}=o;if(e==="custom"&&t)return{mode:x(t.backgroundColor)?"dark":"light",backgroundColor:t.backgroundColor,surfaceColor:t.surfaceColor,textPrimary:t.textPrimary,textSecondary:t.textSecondary,primaryColor:t.primaryColor,primaryHover:t.primaryHover,borderColor:t.borderColor,shadowColor:t.shadowColor,starColor:"#fbbf24"};if(e==="adaptive"){let i=r||{fallback:"light",autoContrast:!0},a=D(i.sourceElement,i.inheritCssVars);return a?{...a.mode==="dark"?u:b,...a}:i.fallback==="dark"?u:b}return e==="auto"?window.matchMedia("(prefers-color-scheme: dark)").matches?u:b:e==="dark"?u:b}function C(o){return`
|
|
2
|
+
--bliptar-bg: ${o.backgroundColor};
|
|
3
|
+
--bliptar-surface: ${o.surfaceColor};
|
|
4
|
+
--bliptar-text: ${o.textPrimary};
|
|
5
|
+
--bliptar-text-secondary: ${o.textSecondary};
|
|
6
|
+
--bliptar-primary: ${o.primaryColor};
|
|
7
|
+
--bliptar-primary-hover: ${o.primaryHover};
|
|
8
|
+
--bliptar-border: ${o.borderColor};
|
|
9
|
+
--bliptar-shadow: ${o.shadowColor};
|
|
10
|
+
--bliptar-star: ${o.starColor};
|
|
11
|
+
`}var f=class{constructor(e,t){this.container=null;this.currentIndex=0;this.answers={};this.form=e,this.options=t,this.theme=k(e.appearance)}render(){this.createContainer(),this.injectStyles(),this.renderQuestion()}destroy(){if(this.container){let e=this.container.querySelector(".bliptar__card");e?(e.classList.add("bliptar__card--exit"),setTimeout(()=>{this.container?.remove(),this.container=null},200)):(this.container.remove(),this.container=null)}}createContainer(){this.container=document.createElement("div"),this.container.id="bliptar-form",this.container.className=`bliptar bliptar--${this.form.appearance.position}`,document.body.appendChild(this.container)}injectStyles(){let e=document.getElementById("bliptar-styles");e&&e.remove();let t=document.createElement("style");t.id="bliptar-styles",t.textContent=this.getStyles(),document.head.appendChild(t)}getStyles(){let{appearance:e}=this.form,t=this.theme,r=e.animation||"slide-up";return`
|
|
12
12
|
:root { ${C(t)} }
|
|
13
13
|
|
|
14
14
|
.bliptar {
|
|
@@ -372,20 +372,20 @@ var u={mode:"light",backgroundColor:"#ffffff",surfaceColor:"#f5f5f5",textPrimary
|
|
|
372
372
|
}
|
|
373
373
|
|
|
374
374
|
@media print { .bliptar { display: none !important; } }
|
|
375
|
-
`}getAnimationStyles(e){switch(e){case"slide-up":return"animation: bliptar-slide-up 0.35s cubic-bezier(0.16, 1, 0.3, 1);";case"fade":return"animation: bliptar-fade 0.3s ease-out;";case"scale":return"animation: bliptar-scale 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);";case"none":return"";default:return"animation: bliptar-slide-up 0.35s cubic-bezier(0.16, 1, 0.3, 1);"}}renderQuestion(){if(!this.container)return;let{questions:e,appearance:t,behavior:r}=this.form,
|
|
375
|
+
`}getAnimationStyles(e){switch(e){case"slide-up":return"animation: bliptar-slide-up 0.35s cubic-bezier(0.16, 1, 0.3, 1);";case"fade":return"animation: bliptar-fade 0.3s ease-out;";case"scale":return"animation: bliptar-scale 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);";case"none":return"";default:return"animation: bliptar-slide-up 0.35s cubic-bezier(0.16, 1, 0.3, 1);"}}renderQuestion(){if(!this.container)return;let{questions:e,appearance:t,behavior:r}=this.form,i=e[this.currentIndex],a=this.currentIndex===e.length-1,n="";if(t.backdrop&&(n+='<div class="bliptar__backdrop"></div>'),n+='<div class="bliptar__card">',r.allowDismiss&&(n+=`<button class="bliptar__close" aria-label="Close">
|
|
376
376
|
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
|
|
377
377
|
<path d="M1 1l12 12M1 13L13 1"/>
|
|
378
378
|
</svg>
|
|
379
|
-
</button>`),r.showProgress&&e.length>1){n+='<div class="bliptar__progress">';for(let l=0;l<e.length;l++)n+=`<div class="bliptar__progress-bar${l<=this.currentIndex?" bliptar__progress-bar--active":""}"></div>`;n+="</div>"}n+=`<div class="bliptar__question">${this.escapeHtml(
|
|
379
|
+
</button>`),r.showProgress&&e.length>1){n+='<div class="bliptar__progress">';for(let l=0;l<e.length;l++)n+=`<div class="bliptar__progress-bar${l<=this.currentIndex?" bliptar__progress-bar--active":""}"></div>`;n+="</div>"}n+=`<div class="bliptar__question">${this.escapeHtml(i.question)}</div>`,n+=this.renderAnswerOptions(i),(!r.autoAdvance||i.type==="text-input")&&(n+='<div class="bliptar__nav">',n+=this.currentIndex>0?'<button class="bliptar__nav-btn bliptar__nav-btn--back">Back</button>':"<div></div>",n+=`<button class="bliptar__nav-btn bliptar__nav-btn--next">${a?"Submit":"Continue"}</button>`,n+="</div>"),n+="</div>",this.container.innerHTML=n,this.attachEventListeners()}escapeHtml(e){let t=document.createElement("div");return t.textContent=e,t.innerHTML}renderAnswerOptions(e){let t=`q${this.currentIndex}`,r=this.answers[t];switch(e.type){case"binary":return`<div class="bliptar__options">
|
|
380
380
|
<button class="bliptar__btn${r===!0?" bliptar__btn--selected":""}" data-value="true">
|
|
381
381
|
${e.style==="thumbs"?"\u{1F44D} ":""}${this.escapeHtml(e.positiveLabel)}
|
|
382
382
|
</button>
|
|
383
383
|
<button class="bliptar__btn${r===!1?" bliptar__btn--selected":""}" data-value="false">
|
|
384
384
|
${e.style==="thumbs"?"\u{1F44E} ":""}${this.escapeHtml(e.negativeLabel)}
|
|
385
385
|
</button>
|
|
386
|
-
</div>`;case"star-rating":let
|
|
386
|
+
</div>`;case"star-rating":let i='<div class="bliptar__stars">';for(let s=1;s<=e.maxStars;s++)i+=`<span class="bliptar__star${r>=s?" bliptar__star--active":""}" data-value="${s}">\u2605</span>`;return i+"</div>";case"emoji-scale":let a=["\u{1F61E}","\u{1F615}","\u{1F610}","\u{1F642}","\u{1F60A}"],n=["\u{1F61E}","\u{1F610}","\u{1F60A}"],l=e.scale===3?n:a,c='<div class="bliptar__emojis">';return l.forEach((s,d)=>{c+=`<span class="bliptar__emoji${r===d+1?" bliptar__emoji--active":""}" data-value="${d+1}" title="${e.labels?.[d]||""}">${s}</span>`}),c+"</div>";case"nps":let p='<div class="bliptar__nps">';for(let s=0;s<=10;s++)p+=`<button class="bliptar__nps-btn${r===s?" bliptar__nps-btn--selected":""}" data-value="${s}">${s}</button>`;return p+=`</div><div class="bliptar__nps-labels"><span>${this.escapeHtml(e.lowLabel)}</span><span>${this.escapeHtml(e.highLabel)}</span></div>`,p;case"single-choice":let h='<div class="bliptar__options">';return e.options.forEach(s=>{h+=`<button class="bliptar__btn${r===s?" bliptar__btn--selected":""}" data-value="${this.escapeHtml(s)}">${this.escapeHtml(s)}</button>`}),h+"</div>";case"text-input":return`<textarea class="bliptar__textarea" placeholder="${this.escapeHtml(e.placeholder)}" maxlength="${e.maxLength}">${r||""}</textarea>`;default:return""}}attachEventListeners(){if(!this.container)return;let e=this.form.questions[this.currentIndex],{behavior:t}=this.form,r=this.container.querySelector(".bliptar__close");r&&r.addEventListener("click",()=>this.options.onDismiss());let i=this.container.querySelector(".bliptar__backdrop");i&&t.allowDismiss&&i.addEventListener("click",()=>this.options.onDismiss());let a=s=>{this.answers[`q${this.currentIndex}`]=s,t.autoAdvance&&e.type!=="text-input"?setTimeout(()=>this.goNext(),t.autoAdvanceDelay||300):this.renderQuestion()};this.container.querySelectorAll(".bliptar__btn, .bliptar__nps-btn").forEach(s=>{s.addEventListener("click",()=>{let d=s.dataset.value;e.type==="binary"?a(d==="true"):e.type==="nps"?a(parseInt(d,10)):a(d)})});let n=this.container.querySelectorAll(".bliptar__star");n.forEach((s,d)=>{s.addEventListener("mouseenter",()=>{n.forEach((v,S)=>v.classList.toggle("bliptar__star--active",S<=d))}),s.addEventListener("click",()=>a(parseInt(s.dataset.value,10)))});let l=this.container.querySelector(".bliptar__stars");l&&l.addEventListener("mouseleave",()=>{let s=this.answers[`q${this.currentIndex}`];n.forEach((d,v)=>d.classList.toggle("bliptar__star--active",s?v<s:!1))}),this.container.querySelectorAll(".bliptar__emoji").forEach(s=>{s.addEventListener("click",()=>a(parseInt(s.dataset.value,10)))});let c=this.container.querySelector(".bliptar__textarea");c&&(c.addEventListener("input",()=>{this.answers[`q${this.currentIndex}`]=c.value}),setTimeout(()=>c.focus(),100));let p=this.container.querySelector(".bliptar__nav-btn--back");p&&p.addEventListener("click",()=>this.goBack());let h=this.container.querySelector(".bliptar__nav-btn--next");h&&h.addEventListener("click",()=>this.goNext())}goBack(){this.currentIndex>0&&(this.currentIndex--,this.renderQuestion())}async goNext(){this.currentIndex===this.form.questions.length-1?await this.submit():(this.currentIndex++,this.renderQuestion())}async submit(){if(this.container){let e=this.container.querySelector(".bliptar__card");e&&(e.innerHTML='<div class="bliptar__loading"><div class="bliptar__spinner"></div></div>')}try{await this.options.onSubmit(this.answers),this.form.behavior.showThankYou?(this.renderThankYou(),setTimeout(()=>this.options.onDismiss(),this.form.behavior.thankYouDuration||2e3)):this.options.onDismiss()}catch{this.options.onDismiss()}}renderThankYou(){if(!this.container)return;let{appearance:e,behavior:t}=this.form,r="";e.backdrop&&(r+='<div class="bliptar__backdrop"></div>'),r+=`<div class="bliptar__card">
|
|
387
387
|
<div class="bliptar__thankyou">
|
|
388
388
|
<div class="bliptar__thankyou-emoji">\u{1F389}</div>
|
|
389
389
|
<div class="bliptar__thankyou-text">${this.escapeHtml(t.thankYouMessage||"Thank you for your feedback!")}</div>
|
|
390
390
|
</div>
|
|
391
|
-
</div>`,this.container.innerHTML=r}};function w(){let
|
|
391
|
+
</div>`,this.container.innerHTML=r}};function w(){let o="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",e="";for(let t=0;t<16;t++)e+=o.charAt(Math.floor(Math.random()*o.length));return`sess_${e}`}function _(){let o=navigator.userAgent;return/tablet|ipad|playbook|silk/i.test(o)?"tablet":/mobile|iphone|ipod|android|blackberry|opera mini|iemobile/i.test(o)?"mobile":"desktop"}function T(o,e){let t;return(...r)=>{clearTimeout(t),t=setTimeout(()=>o(...r),e)}}var A="https://api.bliptar.com",m=class{constructor(e){this.renderer=null;this.scrollDepth=0;this.maxScrollDepth=0;this.isInitialized=!1;this.eventListeners=[];this.trackedClicks=new Set;this.exitIntentTriggered=!1;this.exitIntentCooldownTimer=null;this.timeOnPageTimers=[];this.scrollDepthThresholds=new Set;this.triggeredScrollThresholds=new Set;this.campaigns=[];this.config={apiUrl:A,debug:!1,onShow:()=>{},onSubmit:()=>{},onDismiss:()=>{},onError:()=>{},...e},this.sessionId=this.loadOrCreateSessionId(),this.pageLoadTime=Date.now()}async init(){if(this.isInitialized)return;this.isInitialized=!0,this.log("Initializing Bliptar SDK"),await this.fetchConfigAndSetupTriggers();let e=T(()=>{let t=document.documentElement,r=window.scrollY||t.scrollTop,i=t.scrollHeight-t.clientHeight;this.scrollDepth=i>0?r/i:0;let a=Math.round(this.scrollDepth*100);a>this.maxScrollDepth&&(this.maxScrollDepth=a,this.checkScrollThresholds(a))},100);this.addEventListener(window,"scroll",e),this.track("page_view")}async fetchConfigAndSetupTriggers(){try{let e=await fetch(`${this.config.apiUrl}/api/sdk/config`,{headers:{Authorization:`Bearer ${this.config.apiKey}`}});if(!e.ok){this.log("Failed to fetch config:",e.status);return}let t=await e.json();this.campaigns=t.campaigns||[],this.log("Loaded campaigns:",this.campaigns.length),this.setupTriggersFromCampaigns()}catch(e){this.handleError(e)}}setupTriggersFromCampaigns(){let e=new Set;for(let t of this.campaigns){if(e.add(t.trigger_event),t.trigger_event==="scroll_depth"){let r=t.trigger_conditions?.find(i=>i.field==="scroll_depth_percent");r?.value&&this.scrollDepthThresholds.add(Number(r.value))}if(t.trigger_event==="time_on_page"){let r=t.trigger_conditions?.find(i=>i.field==="time_on_page_seconds");if(r?.value){let i=Number(r.value);this.setupTimeOnPageTrigger(i)}}}this.log("Active trigger types:",Array.from(e)),e.has("exit_intent")&&(this.log("Auto-enabling exit intent detection"),this.enableExitIntent({once:!0})),this.scrollDepthThresholds.size>0&&this.log("Scroll depth thresholds:",Array.from(this.scrollDepthThresholds))}setupTimeOnPageTrigger(e){this.log("Setting up time on page trigger:",e,"seconds");let t=setTimeout(()=>{this.track("time_on_page",{seconds:e,actual_time:Math.floor((Date.now()-this.pageLoadTime)/1e3)})},e*1e3);this.timeOnPageTimers.push(t)}checkScrollThresholds(e){for(let t of this.scrollDepthThresholds)e>=t&&!this.triggeredScrollThresholds.has(t)&&(this.triggeredScrollThresholds.add(t),this.log("Scroll threshold reached:",t+"%"),this.track("scroll_depth",{threshold:t,actual_depth:e}))}identify(e,t){this.config.userId=e,this.config.userAttributes={...this.config.userAttributes,...t},this.log("User identified:",e)}async track(e,t){this.log("Tracking event:",e,t);let r=this.getContext();try{let i=await fetch(`${this.config.apiUrl}/api/sdk/trigger`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.config.apiKey}`},body:JSON.stringify({session_id:this.sessionId,user_id:this.config.userId,event:e,event_data:t||{},context:r})});if(!i.ok)throw new Error(`API error: ${i.status}`);let a=await i.json();a.show_form&&a.form&&await this.displayForm(a.form,a.campaign_id)}catch(i){this.handleError(i)}}async showForm(e){this.log("Manually showing form:",e);try{let t=await fetch(`${this.config.apiUrl}/api/sdk/form/${e}`,{headers:{Authorization:`Bearer ${this.config.apiKey}`}});if(!t.ok)throw new Error(`Failed to fetch form: ${t.status}`);let r=await t.json();await this.displayForm(r)}catch(t){this.handleError(t)}}trackClicks(e){let{selector:t,name:r,once:i=!1}=e,a=r||t;this.log("Setting up click tracking for:",t);let n=l=>{let p=l.target.closest(t);p&&(i&&this.trackedClicks.has(a)||(this.log("Button click detected:",a),i&&this.trackedClicks.add(a),this.track("button_click",{selector:t,name:a,element_text:p.textContent?.trim().slice(0,100)||"",element_id:p.id||null,element_class:p.className||null})))};this.addEventListener(document,"click",n)}enableExitIntent(e={}){let{threshold:t=50,cooldown:r=1e4,once:i=!0}=e;this.log("Enabling exit intent detection");let a=l=>{l.clientY>t||i&&this.exitIntentTriggered||this.exitIntentCooldownTimer||(this.log("Exit intent detected"),this.exitIntentTriggered=!0,this.track("exit_intent",{trigger:"mouse_leave",mouse_y:l.clientY}),i||(this.exitIntentCooldownTimer=setTimeout(()=>{this.exitIntentCooldownTimer=null},r)))};this.addEventListener(document,"mouseleave",a);let n=()=>{if(document.visibilityState==="hidden"){if(i&&this.exitIntentTriggered)return;this.log("Exit intent detected (visibility change)"),this.exitIntentTriggered=!0,this.track("exit_intent",{trigger:"visibility_change"})}};_()!=="desktop"&&this.addEventListener(document,"visibilitychange",n)}destroy(){this.log("Destroying Bliptar SDK"),this.eventListeners.forEach(({target:e,type:t,handler:r})=>{e.removeEventListener(t,r)}),this.eventListeners=[],this.renderer&&(this.renderer.destroy(),this.renderer=null),this.exitIntentCooldownTimer&&(clearTimeout(this.exitIntentCooldownTimer),this.exitIntentCooldownTimer=null),this.timeOnPageTimers.forEach(e=>clearTimeout(e)),this.timeOnPageTimers=[],this.trackedClicks.clear(),this.exitIntentTriggered=!1,this.scrollDepthThresholds.clear(),this.triggeredScrollThresholds.clear(),this.campaigns=[],this.maxScrollDepth=0,this.isInitialized=!1}async displayForm(e,t){this.log("Displaying form:",e.formId),this.config.onShow?.(e),this.renderer=new f(e,{onSubmit:async r=>{await this.submitResponse(e.formId,r,t),this.config.onSubmit?.(r)},onDismiss:()=>{this.config.onDismiss?.(),this.renderer?.destroy(),this.renderer=null}}),this.renderer.render()}async submitResponse(e,t,r){this.log("Submitting response:",e,t);let i=this.getContext(),a=new Date().toISOString();try{await fetch(`${this.config.apiUrl}/api/sdk/submit`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.config.apiKey}`},body:JSON.stringify({session_id:this.sessionId,user_id:this.config.userId,form_id:e,campaign_id:r,answers:t,context:i,completed_at:a})})}catch(n){this.handleError(n)}}getContext(){return{pageUrl:window.location.href,referrer:document.referrer,timeOnPage:Math.floor((Date.now()-this.pageLoadTime)/1e3),scrollDepth:Math.round(this.scrollDepth*100)/100,deviceType:_()}}loadOrCreateSessionId(){let e="bliptar_session",t=sessionStorage.getItem(e);return t||(t=w(),sessionStorage.setItem(e,t)),t}addEventListener(e,t,r){e.addEventListener(t,r),this.eventListeners.push({target:e,type:t,handler:r})}log(...e){this.config.debug&&console.log("[Bliptar]",...e)}handleError(e){this.log("Error:",e),this.config.onError?.(e)}};function $(o){return new m(o)}if(typeof window<"u"){let o=document.currentScript;if(o?.dataset.apiKey){let e=$({apiKey:o.dataset.apiKey,debug:o.dataset.debug==="true"});window.bliptar=e,e.init()}}export{m as Bliptar,$ as createBliptar};
|