@bliptarjs/sdk 0.2.1 → 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 -405
- 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,395 +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
|
-
debug: false,
|
|
56
|
-
userId: 'user-123',
|
|
57
|
-
userAttributes: {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
|
62
89
|
});
|
|
63
90
|
```
|
|
64
91
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
| Option | Type | Default | Description |
|
|
68
|
-
|--------|------|---------|-------------|
|
|
69
|
-
| `apiKey` | `string` | **required** | Your Bliptar API key |
|
|
70
|
-
| `debug` | `boolean` | `false` | Enable console logging for debugging |
|
|
71
|
-
| `userId` | `string` | - | Pre-set user ID for attribution |
|
|
72
|
-
| `userAttributes` | `object` | - | Pre-set user attributes for targeting |
|
|
73
|
-
| `onShow` | `function` | - | Callback when a form is displayed |
|
|
74
|
-
| `onSubmit` | `function` | - | Callback when a response is submitted |
|
|
75
|
-
| `onDismiss` | `function` | - | Callback when a form is dismissed |
|
|
76
|
-
| `onError` | `function` | - | Callback when an error occurs |
|
|
77
|
-
|
|
78
|
-
---
|
|
92
|
+
## API Reference
|
|
79
93
|
|
|
80
94
|
### `init()`
|
|
81
95
|
|
|
82
|
-
Initializes the SDK.
|
|
96
|
+
Initializes the SDK. Fetches campaigns and sets up automatic triggers.
|
|
83
97
|
|
|
84
98
|
```javascript
|
|
85
|
-
bliptar.init();
|
|
99
|
+
await bliptar.init();
|
|
86
100
|
```
|
|
87
101
|
|
|
88
|
-
> **Note:** Call this once after creating the instance, typically when your app loads.
|
|
89
|
-
|
|
90
|
-
---
|
|
91
|
-
|
|
92
102
|
### `identify(userId, attributes?)`
|
|
93
103
|
|
|
94
|
-
Associates feedback with a
|
|
104
|
+
Associates feedback with a user. Call when user logs in.
|
|
95
105
|
|
|
96
106
|
```javascript
|
|
97
|
-
// Basic identification
|
|
98
|
-
bliptar.identify('user-123');
|
|
99
|
-
|
|
100
|
-
// With attributes for targeting
|
|
101
107
|
bliptar.identify('user-123', {
|
|
102
108
|
email: 'user@example.com',
|
|
103
109
|
plan: 'pro',
|
|
104
|
-
company: 'Acme Inc',
|
|
105
|
-
signupDate: '2024-01-15',
|
|
106
110
|
});
|
|
107
111
|
```
|
|
108
112
|
|
|
109
|
-
User attributes can be used in campaign targeting rules (e.g., only show to "pro" plan users).
|
|
110
|
-
|
|
111
|
-
---
|
|
112
|
-
|
|
113
113
|
### `track(event, data?)`
|
|
114
114
|
|
|
115
|
-
|
|
115
|
+
Triggers a custom event. Use with "Custom Event" campaign trigger.
|
|
116
116
|
|
|
117
117
|
```javascript
|
|
118
|
-
// Simple event
|
|
119
|
-
bliptar.track('checkout_complete');
|
|
120
|
-
|
|
121
|
-
// With event data
|
|
122
118
|
bliptar.track('checkout_complete', {
|
|
123
|
-
orderId: 'order-
|
|
119
|
+
orderId: 'order-123',
|
|
124
120
|
total: 99.99,
|
|
125
|
-
items: 3,
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
// Feature usage
|
|
129
|
-
bliptar.track('feature_used', {
|
|
130
|
-
feature: 'export_pdf',
|
|
131
|
-
duration: 5000,
|
|
132
121
|
});
|
|
133
122
|
```
|
|
134
123
|
|
|
135
|
-
Configure campaigns in the dashboard to trigger forms based on these events.
|
|
136
|
-
|
|
137
|
-
---
|
|
138
|
-
|
|
139
|
-
### `trackClicks(options)`
|
|
140
|
-
|
|
141
|
-
Sets up click tracking on specific elements. When a user clicks a matching element, it fires a `button_click` event that can trigger campaigns.
|
|
142
|
-
|
|
143
|
-
```javascript
|
|
144
|
-
// Track clicks on a specific button
|
|
145
|
-
bliptar.trackClicks({
|
|
146
|
-
selector: '#upgrade-button',
|
|
147
|
-
name: 'upgrade-cta',
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
// Track clicks on all pricing buttons
|
|
151
|
-
bliptar.trackClicks({
|
|
152
|
-
selector: '.pricing-cta',
|
|
153
|
-
name: 'pricing-click',
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
// Track only the first click
|
|
157
|
-
bliptar.trackClicks({
|
|
158
|
-
selector: '.signup-button',
|
|
159
|
-
name: 'signup-click',
|
|
160
|
-
once: true,
|
|
161
|
-
});
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
#### Options
|
|
165
|
-
|
|
166
|
-
| Option | Type | Default | Description |
|
|
167
|
-
|--------|------|---------|-------------|
|
|
168
|
-
| `selector` | `string` | **required** | CSS selector for elements to track |
|
|
169
|
-
| `name` | `string` | `selector` | Friendly name for the click target |
|
|
170
|
-
| `once` | `boolean` | `false` | Only track the first click per session |
|
|
171
|
-
|
|
172
|
-
#### Event Data Sent
|
|
173
|
-
|
|
174
|
-
When a click is detected, the following data is sent:
|
|
175
|
-
|
|
176
|
-
```javascript
|
|
177
|
-
{
|
|
178
|
-
selector: '.pricing-cta',
|
|
179
|
-
name: 'pricing-click',
|
|
180
|
-
element_text: 'Get Started',
|
|
181
|
-
element_id: 'cta-btn',
|
|
182
|
-
element_class: 'pricing-cta btn-primary'
|
|
183
|
-
}
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
---
|
|
187
|
-
|
|
188
|
-
### `enableExitIntent(options?)`
|
|
189
|
-
|
|
190
|
-
Enables exit intent detection. Triggers an `exit_intent` event when a user appears to be leaving the page.
|
|
191
|
-
|
|
192
|
-
```javascript
|
|
193
|
-
// Basic usage (triggers once per session)
|
|
194
|
-
bliptar.enableExitIntent();
|
|
195
|
-
|
|
196
|
-
// Custom configuration
|
|
197
|
-
bliptar.enableExitIntent({
|
|
198
|
-
threshold: 50, // Pixels from top of viewport
|
|
199
|
-
cooldown: 10000, // Ms before can trigger again
|
|
200
|
-
once: true, // Only trigger once per session
|
|
201
|
-
});
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
#### Options
|
|
205
|
-
|
|
206
|
-
| Option | Type | Default | Description |
|
|
207
|
-
|--------|------|---------|-------------|
|
|
208
|
-
| `threshold` | `number` | `50` | Distance in pixels from top of viewport to trigger |
|
|
209
|
-
| `cooldown` | `number` | `10000` | Milliseconds before exit intent can trigger again |
|
|
210
|
-
| `once` | `boolean` | `true` | Only trigger once per session |
|
|
211
|
-
|
|
212
|
-
#### How It Works
|
|
213
|
-
|
|
214
|
-
- **Desktop:** Detects when the mouse cursor leaves the viewport from the top (user moving to close tab, click back, or navigate away)
|
|
215
|
-
- **Mobile:** Detects when the page visibility changes (user switching apps or tabs)
|
|
216
|
-
|
|
217
|
-
---
|
|
218
|
-
|
|
219
124
|
### `showForm(formId)`
|
|
220
125
|
|
|
221
|
-
Manually
|
|
126
|
+
Manually show a specific form, bypassing campaign rules.
|
|
222
127
|
|
|
223
128
|
```javascript
|
|
224
|
-
// Show a form programmatically
|
|
225
129
|
await bliptar.showForm('form_abc123');
|
|
226
|
-
|
|
227
|
-
// Example: Show after completing onboarding
|
|
228
|
-
function completeOnboarding() {
|
|
229
|
-
// ... onboarding logic
|
|
230
|
-
bliptar.showForm('form_onboarding_feedback');
|
|
231
|
-
}
|
|
232
130
|
```
|
|
233
131
|
|
|
234
|
-
---
|
|
235
|
-
|
|
236
132
|
### `destroy()`
|
|
237
133
|
|
|
238
|
-
|
|
134
|
+
Cleanup - removes listeners and timers. Call when unmounting.
|
|
239
135
|
|
|
240
136
|
```javascript
|
|
241
137
|
bliptar.destroy();
|
|
242
138
|
```
|
|
243
139
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
## Usage Examples
|
|
247
|
-
|
|
248
|
-
### Basic Setup
|
|
249
|
-
|
|
250
|
-
```javascript
|
|
251
|
-
import { createBliptar } from '@bliptarjs/sdk';
|
|
252
|
-
|
|
253
|
-
const bliptar = createBliptar({
|
|
254
|
-
apiKey: 'blip_live_xxxxxxxx',
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
bliptar.init();
|
|
258
|
-
```
|
|
259
|
-
|
|
260
|
-
### With User Identification
|
|
261
|
-
|
|
262
|
-
```javascript
|
|
263
|
-
import { createBliptar } from '@bliptarjs/sdk';
|
|
264
|
-
|
|
265
|
-
const bliptar = createBliptar({
|
|
266
|
-
apiKey: 'blip_live_xxxxxxxx',
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
bliptar.init();
|
|
270
|
-
|
|
271
|
-
// When user logs in
|
|
272
|
-
function onUserLogin(user) {
|
|
273
|
-
bliptar.identify(user.id, {
|
|
274
|
-
email: user.email,
|
|
275
|
-
plan: user.subscription.plan,
|
|
276
|
-
createdAt: user.createdAt,
|
|
277
|
-
});
|
|
278
|
-
}
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
### E-commerce Checkout Feedback
|
|
282
|
-
|
|
283
|
-
```javascript
|
|
284
|
-
import { createBliptar } from '@bliptarjs/sdk';
|
|
285
|
-
|
|
286
|
-
const bliptar = createBliptar({
|
|
287
|
-
apiKey: 'blip_live_xxxxxxxx',
|
|
288
|
-
onSubmit: (answers) => {
|
|
289
|
-
// Send to your analytics
|
|
290
|
-
analytics.track('feedback_submitted', answers);
|
|
291
|
-
},
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
bliptar.init();
|
|
295
|
-
|
|
296
|
-
// Track successful purchase
|
|
297
|
-
function onPurchaseComplete(order) {
|
|
298
|
-
bliptar.track('purchase_complete', {
|
|
299
|
-
orderId: order.id,
|
|
300
|
-
total: order.total,
|
|
301
|
-
itemCount: order.items.length,
|
|
302
|
-
});
|
|
303
|
-
}
|
|
304
|
-
```
|
|
305
|
-
|
|
306
|
-
### Exit Intent Survey
|
|
307
|
-
|
|
308
|
-
```javascript
|
|
309
|
-
import { createBliptar } from '@bliptarjs/sdk';
|
|
310
|
-
|
|
311
|
-
const bliptar = createBliptar({
|
|
312
|
-
apiKey: 'blip_live_xxxxxxxx',
|
|
313
|
-
});
|
|
314
|
-
|
|
315
|
-
bliptar.init();
|
|
316
|
-
|
|
317
|
-
// Enable exit intent on pricing page
|
|
318
|
-
if (window.location.pathname === '/pricing') {
|
|
319
|
-
bliptar.enableExitIntent({
|
|
320
|
-
once: true, // Only show once
|
|
321
|
-
});
|
|
322
|
-
}
|
|
323
|
-
```
|
|
324
|
-
|
|
325
|
-
### Button Click Feedback
|
|
140
|
+
## Framework Examples
|
|
326
141
|
|
|
327
|
-
|
|
328
|
-
import { createBliptar } from '@bliptarjs/sdk';
|
|
329
|
-
|
|
330
|
-
const bliptar = createBliptar({
|
|
331
|
-
apiKey: 'blip_live_xxxxxxxx',
|
|
332
|
-
});
|
|
333
|
-
|
|
334
|
-
bliptar.init();
|
|
335
|
-
|
|
336
|
-
// Track clicks on upgrade buttons
|
|
337
|
-
bliptar.trackClicks({
|
|
338
|
-
selector: '[data-track="upgrade"]',
|
|
339
|
-
name: 'upgrade-button',
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
// Track clicks on help buttons
|
|
343
|
-
bliptar.trackClicks({
|
|
344
|
-
selector: '.help-button',
|
|
345
|
-
name: 'help-click',
|
|
346
|
-
});
|
|
347
|
-
```
|
|
348
|
-
|
|
349
|
-
### React Integration
|
|
142
|
+
### React
|
|
350
143
|
|
|
351
144
|
```jsx
|
|
352
145
|
import { useEffect } from 'react';
|
|
353
146
|
import { createBliptar } from '@bliptarjs/sdk';
|
|
354
147
|
|
|
355
148
|
const bliptar = createBliptar({
|
|
356
|
-
apiKey:
|
|
149
|
+
apiKey: process.env.REACT_APP_BLIPTAR_KEY,
|
|
357
150
|
});
|
|
358
151
|
|
|
359
152
|
function App() {
|
|
360
153
|
useEffect(() => {
|
|
361
154
|
bliptar.init();
|
|
362
|
-
|
|
363
|
-
return () => {
|
|
364
|
-
bliptar.destroy();
|
|
365
|
-
};
|
|
155
|
+
return () => bliptar.destroy();
|
|
366
156
|
}, []);
|
|
367
157
|
|
|
368
158
|
return <div>Your app</div>;
|
|
369
159
|
}
|
|
370
|
-
|
|
371
|
-
// In a component that needs to identify users
|
|
372
|
-
function UserProfile({ user }) {
|
|
373
|
-
useEffect(() => {
|
|
374
|
-
if (user) {
|
|
375
|
-
bliptar.identify(user.id, {
|
|
376
|
-
email: user.email,
|
|
377
|
-
plan: user.plan,
|
|
378
|
-
});
|
|
379
|
-
}
|
|
380
|
-
}, [user]);
|
|
381
|
-
|
|
382
|
-
return <div>Welcome, {user.name}</div>;
|
|
383
|
-
}
|
|
384
160
|
```
|
|
385
161
|
|
|
386
|
-
### Next.js
|
|
162
|
+
### Next.js
|
|
387
163
|
|
|
388
164
|
```jsx
|
|
389
|
-
//
|
|
165
|
+
// app/providers.js
|
|
166
|
+
'use client';
|
|
167
|
+
import { useEffect } from 'react';
|
|
390
168
|
import { createBliptar } from '@bliptarjs/sdk';
|
|
391
169
|
|
|
392
|
-
|
|
393
|
-
? createBliptar({ apiKey: process.env.
|
|
170
|
+
const bliptar = typeof window !== 'undefined'
|
|
171
|
+
? createBliptar({ apiKey: process.env.NEXT_PUBLIC_BLIPTAR_KEY })
|
|
394
172
|
: null;
|
|
395
173
|
|
|
396
|
-
|
|
397
|
-
'use client';
|
|
398
|
-
|
|
399
|
-
import { useEffect } from 'react';
|
|
400
|
-
import { bliptar } from '@/lib/bliptar';
|
|
401
|
-
|
|
402
|
-
export default function RootLayout({ children }) {
|
|
174
|
+
export function BliptarProvider({ children }) {
|
|
403
175
|
useEffect(() => {
|
|
404
176
|
bliptar?.init();
|
|
405
|
-
|
|
406
|
-
return () => {
|
|
407
|
-
bliptar?.destroy();
|
|
408
|
-
};
|
|
177
|
+
return () => bliptar?.destroy();
|
|
409
178
|
}, []);
|
|
410
179
|
|
|
180
|
+
return children;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// app/layout.js
|
|
184
|
+
import { BliptarProvider } from './providers';
|
|
185
|
+
|
|
186
|
+
export default function RootLayout({ children }) {
|
|
411
187
|
return (
|
|
412
188
|
<html>
|
|
413
|
-
<body>
|
|
189
|
+
<body>
|
|
190
|
+
<BliptarProvider>{children}</BliptarProvider>
|
|
191
|
+
</body>
|
|
414
192
|
</html>
|
|
415
193
|
);
|
|
416
194
|
}
|
|
417
195
|
```
|
|
418
196
|
|
|
419
|
-
### Vue
|
|
197
|
+
### Vue
|
|
420
198
|
|
|
421
199
|
```vue
|
|
422
200
|
<script setup>
|
|
@@ -424,95 +202,78 @@ import { onMounted, onUnmounted } from 'vue';
|
|
|
424
202
|
import { createBliptar } from '@bliptarjs/sdk';
|
|
425
203
|
|
|
426
204
|
const bliptar = createBliptar({
|
|
427
|
-
apiKey:
|
|
205
|
+
apiKey: import.meta.env.VITE_BLIPTAR_KEY,
|
|
428
206
|
});
|
|
429
207
|
|
|
430
|
-
onMounted(() =>
|
|
431
|
-
|
|
432
|
-
});
|
|
433
|
-
|
|
434
|
-
onUnmounted(() => {
|
|
435
|
-
bliptar.destroy();
|
|
436
|
-
});
|
|
208
|
+
onMounted(() => bliptar.init());
|
|
209
|
+
onUnmounted(() => bliptar.destroy());
|
|
437
210
|
</script>
|
|
438
211
|
```
|
|
439
212
|
|
|
440
|
-
|
|
213
|
+
## User Identification
|
|
441
214
|
|
|
442
|
-
|
|
215
|
+
Track feedback by user:
|
|
443
216
|
|
|
444
|
-
|
|
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
|
+
```
|
|
445
225
|
|
|
446
|
-
|
|
447
|
-
|------|-------------|
|
|
448
|
-
| `page_view` | Fired on `init()` |
|
|
449
|
-
| `scroll_depth` | Percentage scrolled (0-100%) |
|
|
450
|
-
| `time_on_page` | Seconds spent on page |
|
|
451
|
-
| `device_type` | `desktop`, `mobile`, or `tablet` |
|
|
452
|
-
| `page_url` | Current page URL |
|
|
453
|
-
| `referrer` | Previous page URL |
|
|
226
|
+
User attributes can be used in campaign targeting rules.
|
|
454
227
|
|
|
455
|
-
|
|
228
|
+
## Advanced: Manual Triggers
|
|
456
229
|
|
|
457
|
-
|
|
230
|
+
For advanced use cases, you can manually set up triggers:
|
|
458
231
|
|
|
459
|
-
|
|
232
|
+
### `trackClicks(options)`
|
|
460
233
|
|
|
461
|
-
|
|
234
|
+
Track clicks on specific elements:
|
|
462
235
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
| **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
|
+
```
|
|
471
243
|
|
|
472
|
-
|
|
244
|
+
### `enableExitIntent(options?)`
|
|
245
|
+
|
|
246
|
+
Manually enable exit intent detection:
|
|
473
247
|
|
|
474
|
-
|
|
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
|
+
```
|
|
475
255
|
|
|
476
|
-
## TypeScript
|
|
256
|
+
## TypeScript
|
|
477
257
|
|
|
478
|
-
|
|
258
|
+
Full TypeScript support included:
|
|
479
259
|
|
|
480
260
|
```typescript
|
|
481
|
-
import {
|
|
482
|
-
createBliptar,
|
|
483
|
-
BliptarInstance,
|
|
484
|
-
BliptarConfig,
|
|
485
|
-
ClickTrackingOptions,
|
|
486
|
-
ExitIntentOptions,
|
|
487
|
-
} from '@bliptarjs/sdk';
|
|
261
|
+
import { createBliptar, BliptarConfig } from '@bliptarjs/sdk';
|
|
488
262
|
|
|
489
263
|
const config: BliptarConfig = {
|
|
490
|
-
apiKey: '
|
|
264
|
+
apiKey: 'your-api-key',
|
|
491
265
|
debug: true,
|
|
492
|
-
onSubmit: (answers) => {
|
|
493
|
-
console.log(answers);
|
|
494
|
-
},
|
|
495
266
|
};
|
|
496
267
|
|
|
497
|
-
const bliptar
|
|
268
|
+
const bliptar = createBliptar(config);
|
|
269
|
+
```
|
|
498
270
|
|
|
499
|
-
|
|
500
|
-
const clickOptions: ClickTrackingOptions = {
|
|
501
|
-
selector: '.cta-button',
|
|
502
|
-
name: 'main-cta',
|
|
503
|
-
once: true,
|
|
504
|
-
};
|
|
505
|
-
bliptar.trackClicks(clickOptions);
|
|
271
|
+
## Bundle Size
|
|
506
272
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
threshold: 50,
|
|
510
|
-
once: true,
|
|
511
|
-
};
|
|
512
|
-
bliptar.enableExitIntent(exitOptions);
|
|
513
|
-
```
|
|
273
|
+
- **ESM:** ~26KB (minified)
|
|
274
|
+
- **Gzipped:** ~8KB
|
|
514
275
|
|
|
515
|
-
|
|
276
|
+
Zero dependencies.
|
|
516
277
|
|
|
517
278
|
## Browser Support
|
|
518
279
|
|
|
@@ -523,44 +284,22 @@ bliptar.enableExitIntent(exitOptions);
|
|
|
523
284
|
| Safari | Latest 2 versions |
|
|
524
285
|
| Edge | Latest 2 versions |
|
|
525
286
|
|
|
526
|
-
---
|
|
527
|
-
|
|
528
|
-
## Bundle Size
|
|
529
|
-
|
|
530
|
-
- **ESM:** ~25KB (minified)
|
|
531
|
-
- **Gzipped:** ~7KB
|
|
532
|
-
|
|
533
|
-
The SDK has zero dependencies.
|
|
534
|
-
|
|
535
|
-
---
|
|
536
|
-
|
|
537
287
|
## Troubleshooting
|
|
538
288
|
|
|
539
289
|
### Forms not showing?
|
|
540
290
|
|
|
541
|
-
1. **Check
|
|
542
|
-
2. **Check
|
|
543
|
-
3. **Enable debug mode** -
|
|
544
|
-
4. **Check throttling** -
|
|
545
|
-
|
|
546
|
-
### 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
|
|
547
295
|
|
|
548
296
|
```javascript
|
|
549
297
|
const bliptar = createBliptar({
|
|
550
|
-
apiKey: '
|
|
551
|
-
debug: true, //
|
|
298
|
+
apiKey: 'your-key',
|
|
299
|
+
debug: true, // See what's happening
|
|
552
300
|
});
|
|
553
301
|
```
|
|
554
302
|
|
|
555
|
-
This will log:
|
|
556
|
-
- SDK initialization
|
|
557
|
-
- Events being tracked
|
|
558
|
-
- Form display triggers
|
|
559
|
-
- API responses
|
|
560
|
-
- Errors
|
|
561
|
-
|
|
562
|
-
---
|
|
563
|
-
|
|
564
303
|
## License
|
|
565
304
|
|
|
566
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};
|