mixpanel-browser 2.41.0 → 2.45.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/.github/workflows/tests.yml +25 -0
- package/CHANGELOG.md +18 -0
- package/README.md +4 -2
- package/dist/mixpanel-jslib-snippet.min.js +3 -3
- package/dist/mixpanel-jslib-snippet.min.test.js +3 -3
- package/dist/mixpanel.amd.js +975 -2843
- package/dist/mixpanel.cjs.js +975 -2843
- package/dist/mixpanel.globals.js +975 -2843
- package/dist/mixpanel.min.js +100 -149
- package/dist/mixpanel.umd.js +975 -2843
- package/doc/build-docs.js +16 -0
- package/doc/readme.io/javascript-full-api-reference.md +18 -0
- package/mixpanel-jslib-snippet.js +2 -2
- package/package.json +3 -3
- package/src/config.js +1 -1
- package/src/mixpanel-core.js +76 -129
- package/src/mixpanel-group.js +4 -0
- package/src/mixpanel-persistence.js +0 -21
- package/src/request-batcher.js +47 -10
- package/src/request-queue.js +55 -18
- package/src/utils.js +2 -71
- package/tunnel.log +0 -0
- package/.travis.yml +0 -6
- package/src/mixpanel-notification.js +0 -1309
- package/src/property-filters.js +0 -508
package/dist/mixpanel.umd.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
var Config = {
|
|
8
8
|
DEBUG: false,
|
|
9
|
-
LIB_VERSION: '2.
|
|
9
|
+
LIB_VERSION: '2.45.0'
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
// since es6 imports are static and we run unit tests from the console, window won't be defined when importing this file
|
|
@@ -40,11 +40,11 @@
|
|
|
40
40
|
var toString = ObjProto.toString;
|
|
41
41
|
var hasOwnProperty = ObjProto.hasOwnProperty;
|
|
42
42
|
var windowConsole = window$1.console;
|
|
43
|
-
var navigator
|
|
43
|
+
var navigator = window$1.navigator;
|
|
44
44
|
var document$1 = window$1.document;
|
|
45
45
|
var windowOpera = window$1.opera;
|
|
46
46
|
var screen = window$1.screen;
|
|
47
|
-
var userAgent = navigator
|
|
47
|
+
var userAgent = navigator.userAgent;
|
|
48
48
|
var nativeBind = FuncProto.bind;
|
|
49
49
|
var nativeForEach = ArrayProto.forEach;
|
|
50
50
|
var nativeIndexOf = ArrayProto.indexOf;
|
|
@@ -156,14 +156,6 @@
|
|
|
156
156
|
return bound;
|
|
157
157
|
};
|
|
158
158
|
|
|
159
|
-
_.bind_instance_methods = function(obj) {
|
|
160
|
-
for (var func in obj) {
|
|
161
|
-
if (typeof(obj[func]) === 'function') {
|
|
162
|
-
obj[func] = _.bind(obj[func], obj);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
};
|
|
166
|
-
|
|
167
159
|
/**
|
|
168
160
|
* @param {*=} obj
|
|
169
161
|
* @param {function(...*)=} iterator
|
|
@@ -192,19 +184,6 @@
|
|
|
192
184
|
}
|
|
193
185
|
};
|
|
194
186
|
|
|
195
|
-
_.escapeHTML = function(s) {
|
|
196
|
-
var escaped = s;
|
|
197
|
-
if (escaped && _.isString(escaped)) {
|
|
198
|
-
escaped = escaped
|
|
199
|
-
.replace(/&/g, '&')
|
|
200
|
-
.replace(/</g, '<')
|
|
201
|
-
.replace(/>/g, '>')
|
|
202
|
-
.replace(/"/g, '"')
|
|
203
|
-
.replace(/'/g, ''');
|
|
204
|
-
}
|
|
205
|
-
return escaped;
|
|
206
|
-
};
|
|
207
|
-
|
|
208
187
|
_.extend = function(obj) {
|
|
209
188
|
_.each(slice.call(arguments, 1), function(source) {
|
|
210
189
|
for (var prop in source) {
|
|
@@ -380,33 +359,6 @@
|
|
|
380
359
|
pad(d.getUTCSeconds());
|
|
381
360
|
};
|
|
382
361
|
|
|
383
|
-
_.safewrap = function(f) {
|
|
384
|
-
return function() {
|
|
385
|
-
try {
|
|
386
|
-
return f.apply(this, arguments);
|
|
387
|
-
} catch (e) {
|
|
388
|
-
console.critical('Implementation error. Please turn on debug and contact support@mixpanel.com.');
|
|
389
|
-
if (Config.DEBUG){
|
|
390
|
-
console.critical(e);
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
};
|
|
394
|
-
};
|
|
395
|
-
|
|
396
|
-
_.safewrap_class = function(klass, functions) {
|
|
397
|
-
for (var i = 0; i < functions.length; i++) {
|
|
398
|
-
klass.prototype[functions[i]] = _.safewrap(klass.prototype[functions[i]]);
|
|
399
|
-
}
|
|
400
|
-
};
|
|
401
|
-
|
|
402
|
-
_.safewrap_instance_methods = function(obj) {
|
|
403
|
-
for (var func in obj) {
|
|
404
|
-
if (typeof(obj[func]) === 'function') {
|
|
405
|
-
obj[func] = _.safewrap(obj[func]);
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
};
|
|
409
|
-
|
|
410
362
|
_.strip_empty_properties = function(p) {
|
|
411
363
|
var ret = {};
|
|
412
364
|
_.each(p, function(v, k) {
|
|
@@ -948,10 +900,12 @@
|
|
|
948
900
|
// This is to block various web spiders from executing our JS and
|
|
949
901
|
// sending false tracking data
|
|
950
902
|
var BLOCKED_UA_STRS = [
|
|
903
|
+
'ahrefsbot',
|
|
951
904
|
'baiduspider',
|
|
952
905
|
'bingbot',
|
|
953
906
|
'bingpreview',
|
|
954
907
|
'facebookexternal',
|
|
908
|
+
'petalbot',
|
|
955
909
|
'pinterest',
|
|
956
910
|
'screaming frog',
|
|
957
911
|
'yahoo! slurp',
|
|
@@ -1648,13 +1602,13 @@
|
|
|
1648
1602
|
properties: function() {
|
|
1649
1603
|
return _.extend(_.strip_empty_properties({
|
|
1650
1604
|
'$os': _.info.os(),
|
|
1651
|
-
'$browser': _.info.browser(userAgent, navigator
|
|
1605
|
+
'$browser': _.info.browser(userAgent, navigator.vendor, windowOpera),
|
|
1652
1606
|
'$referrer': document$1.referrer,
|
|
1653
1607
|
'$referring_domain': _.info.referringDomain(document$1.referrer),
|
|
1654
1608
|
'$device': _.info.device(userAgent)
|
|
1655
1609
|
}), {
|
|
1656
1610
|
'$current_url': window$1.location.href,
|
|
1657
|
-
'$browser_version': _.info.browserVersion(userAgent, navigator
|
|
1611
|
+
'$browser_version': _.info.browserVersion(userAgent, navigator.vendor, windowOpera),
|
|
1658
1612
|
'$screen_height': screen.height,
|
|
1659
1613
|
'$screen_width': screen.width,
|
|
1660
1614
|
'mp_lib': 'web',
|
|
@@ -1667,9 +1621,9 @@
|
|
|
1667
1621
|
people_properties: function() {
|
|
1668
1622
|
return _.extend(_.strip_empty_properties({
|
|
1669
1623
|
'$os': _.info.os(),
|
|
1670
|
-
'$browser': _.info.browser(userAgent, navigator
|
|
1624
|
+
'$browser': _.info.browser(userAgent, navigator.vendor, windowOpera)
|
|
1671
1625
|
}), {
|
|
1672
|
-
'$browser_version': _.info.browserVersion(userAgent, navigator
|
|
1626
|
+
'$browser_version': _.info.browserVersion(userAgent, navigator.vendor, windowOpera)
|
|
1673
1627
|
});
|
|
1674
1628
|
},
|
|
1675
1629
|
|
|
@@ -1677,7 +1631,7 @@
|
|
|
1677
1631
|
return _.strip_empty_properties({
|
|
1678
1632
|
'mp_page': page,
|
|
1679
1633
|
'mp_referrer': document$1.referrer,
|
|
1680
|
-
'mp_browser': _.info.browser(userAgent, navigator
|
|
1634
|
+
'mp_browser': _.info.browser(userAgent, navigator.vendor, windowOpera),
|
|
1681
1635
|
'mp_platform': _.info.os()
|
|
1682
1636
|
});
|
|
1683
1637
|
}
|
|
@@ -1688,28 +1642,6 @@
|
|
|
1688
1642
|
return maxlen ? guid.substring(0, maxlen) : guid;
|
|
1689
1643
|
};
|
|
1690
1644
|
|
|
1691
|
-
/**
|
|
1692
|
-
* Check deterministically whether to include or exclude from a feature rollout/test based on the
|
|
1693
|
-
* given string and the desired percentage to include.
|
|
1694
|
-
* @param {String} str - string to run the check against (for instance a project's token)
|
|
1695
|
-
* @param {String} feature - name of feature (for inclusion in hash, to ensure different results
|
|
1696
|
-
* for different features)
|
|
1697
|
-
* @param {Number} percent_allowed - percentage chance that a given string will be included
|
|
1698
|
-
* @returns {Boolean} whether the given string should be included
|
|
1699
|
-
*/
|
|
1700
|
-
var determine_eligibility = _.safewrap(function(str, feature, percent_allowed) {
|
|
1701
|
-
str = str + feature;
|
|
1702
|
-
|
|
1703
|
-
// Bernstein's hash: http://www.cse.yorku.ca/~oz/hash.html#djb2
|
|
1704
|
-
var hash = 5381;
|
|
1705
|
-
for (var i = 0; i < str.length; i++) {
|
|
1706
|
-
hash = ((hash << 5) + hash) + str.charCodeAt(i);
|
|
1707
|
-
hash = hash & hash;
|
|
1708
|
-
}
|
|
1709
|
-
var dart = (hash >>> 0) % 100;
|
|
1710
|
-
return dart < percent_allowed;
|
|
1711
|
-
});
|
|
1712
|
-
|
|
1713
1645
|
// naive way to extract domain name (example.com) from full hostname (my.sub.example.com)
|
|
1714
1646
|
var SIMPLE_DOMAIN_MATCH_REGEX = /[a-z0-9][a-z0-9-]*\.[a-z]+$/i;
|
|
1715
1647
|
// this next one attempts to account for some ccSLDs, e.g. extracting oxford.ac.uk from www.oxford.ac.uk
|
|
@@ -2081,6 +2013,7 @@
|
|
|
2081
2013
|
options = options || {};
|
|
2082
2014
|
this.storageKey = storageKey;
|
|
2083
2015
|
this.storage = options.storage || window.localStorage;
|
|
2016
|
+
this.reportError = options.errorReporter || _.bind(logger$1.error, logger$1);
|
|
2084
2017
|
this.lock = new SharedLock(storageKey, {storage: this.storage});
|
|
2085
2018
|
|
|
2086
2019
|
this.pid = options.pid || null; // pass pid to test out storage lock contention scenarios
|
|
@@ -2118,18 +2051,18 @@
|
|
|
2118
2051
|
this.memQueue.push(queueEntry);
|
|
2119
2052
|
}
|
|
2120
2053
|
} catch(err) {
|
|
2121
|
-
|
|
2054
|
+
this.reportError('Error enqueueing item', item);
|
|
2122
2055
|
succeeded = false;
|
|
2123
2056
|
}
|
|
2124
2057
|
if (cb) {
|
|
2125
2058
|
cb(succeeded);
|
|
2126
2059
|
}
|
|
2127
|
-
}, this), function lockFailure(err) {
|
|
2128
|
-
|
|
2060
|
+
}, this), _.bind(function lockFailure(err) {
|
|
2061
|
+
this.reportError('Error acquiring storage lock', err);
|
|
2129
2062
|
if (cb) {
|
|
2130
2063
|
cb(false);
|
|
2131
2064
|
}
|
|
2132
|
-
}, this.pid);
|
|
2065
|
+
}, this), this.pid);
|
|
2133
2066
|
};
|
|
2134
2067
|
|
|
2135
2068
|
/**
|
|
@@ -2189,25 +2122,61 @@
|
|
|
2189
2122
|
_.each(ids, function(id) { idSet[id] = true; });
|
|
2190
2123
|
|
|
2191
2124
|
this.memQueue = filterOutIDsAndInvalid(this.memQueue, idSet);
|
|
2192
|
-
|
|
2125
|
+
|
|
2126
|
+
var removeFromStorage = _.bind(function() {
|
|
2193
2127
|
var succeeded;
|
|
2194
2128
|
try {
|
|
2195
2129
|
var storedQueue = this.readFromStorage();
|
|
2196
2130
|
storedQueue = filterOutIDsAndInvalid(storedQueue, idSet);
|
|
2197
2131
|
succeeded = this.saveToStorage(storedQueue);
|
|
2132
|
+
|
|
2133
|
+
// an extra check: did storage report success but somehow
|
|
2134
|
+
// the items are still there?
|
|
2135
|
+
if (succeeded) {
|
|
2136
|
+
storedQueue = this.readFromStorage();
|
|
2137
|
+
for (var i = 0; i < storedQueue.length; i++) {
|
|
2138
|
+
var item = storedQueue[i];
|
|
2139
|
+
if (item['id'] && !!idSet[item['id']]) {
|
|
2140
|
+
this.reportError('Item not removed from storage');
|
|
2141
|
+
return false;
|
|
2142
|
+
}
|
|
2143
|
+
}
|
|
2144
|
+
}
|
|
2198
2145
|
} catch(err) {
|
|
2199
|
-
|
|
2146
|
+
this.reportError('Error removing items', ids);
|
|
2200
2147
|
succeeded = false;
|
|
2201
2148
|
}
|
|
2149
|
+
return succeeded;
|
|
2150
|
+
}, this);
|
|
2151
|
+
|
|
2152
|
+
this.lock.withLock(function lockAcquired() {
|
|
2153
|
+
var succeeded = removeFromStorage();
|
|
2202
2154
|
if (cb) {
|
|
2203
2155
|
cb(succeeded);
|
|
2204
2156
|
}
|
|
2205
|
-
},
|
|
2206
|
-
|
|
2157
|
+
}, _.bind(function lockFailure(err) {
|
|
2158
|
+
var succeeded = false;
|
|
2159
|
+
this.reportError('Error acquiring storage lock', err);
|
|
2160
|
+
if (!localStorageSupported(this.storage, true)) {
|
|
2161
|
+
// Looks like localStorage writes have stopped working sometime after
|
|
2162
|
+
// initialization (probably full), and so nobody can acquire locks
|
|
2163
|
+
// anymore. Consider it temporarily safe to remove items without the
|
|
2164
|
+
// lock, since nobody's writing successfully anyway.
|
|
2165
|
+
succeeded = removeFromStorage();
|
|
2166
|
+
if (!succeeded) {
|
|
2167
|
+
// OK, we couldn't even write out the smaller queue. Try clearing it
|
|
2168
|
+
// entirely.
|
|
2169
|
+
try {
|
|
2170
|
+
this.storage.removeItem(this.storageKey);
|
|
2171
|
+
} catch(err) {
|
|
2172
|
+
this.reportError('Error clearing queue', err);
|
|
2173
|
+
}
|
|
2174
|
+
}
|
|
2175
|
+
}
|
|
2207
2176
|
if (cb) {
|
|
2208
|
-
cb(
|
|
2177
|
+
cb(succeeded);
|
|
2209
2178
|
}
|
|
2210
|
-
}, this.pid);
|
|
2179
|
+
}, this), this.pid);
|
|
2211
2180
|
};
|
|
2212
2181
|
|
|
2213
2182
|
// internal helper for RequestQueue.updatePayloads
|
|
@@ -2242,18 +2211,18 @@
|
|
|
2242
2211
|
storedQueue = updatePayloads(storedQueue, itemsToUpdate);
|
|
2243
2212
|
succeeded = this.saveToStorage(storedQueue);
|
|
2244
2213
|
} catch(err) {
|
|
2245
|
-
|
|
2214
|
+
this.reportError('Error updating items', itemsToUpdate);
|
|
2246
2215
|
succeeded = false;
|
|
2247
2216
|
}
|
|
2248
2217
|
if (cb) {
|
|
2249
2218
|
cb(succeeded);
|
|
2250
2219
|
}
|
|
2251
|
-
}, this), function lockFailure(err) {
|
|
2252
|
-
|
|
2220
|
+
}, this), _.bind(function lockFailure(err) {
|
|
2221
|
+
this.reportError('Error acquiring storage lock', err);
|
|
2253
2222
|
if (cb) {
|
|
2254
2223
|
cb(false);
|
|
2255
2224
|
}
|
|
2256
|
-
}, this.pid);
|
|
2225
|
+
}, this), this.pid);
|
|
2257
2226
|
};
|
|
2258
2227
|
|
|
2259
2228
|
/**
|
|
@@ -2267,12 +2236,12 @@
|
|
|
2267
2236
|
if (storageEntry) {
|
|
2268
2237
|
storageEntry = JSONParse(storageEntry);
|
|
2269
2238
|
if (!_.isArray(storageEntry)) {
|
|
2270
|
-
|
|
2239
|
+
this.reportError('Invalid storage entry:', storageEntry);
|
|
2271
2240
|
storageEntry = null;
|
|
2272
2241
|
}
|
|
2273
2242
|
}
|
|
2274
2243
|
} catch (err) {
|
|
2275
|
-
|
|
2244
|
+
this.reportError('Error retrieving queue', err);
|
|
2276
2245
|
storageEntry = null;
|
|
2277
2246
|
}
|
|
2278
2247
|
return storageEntry || [];
|
|
@@ -2286,7 +2255,7 @@
|
|
|
2286
2255
|
this.storage.setItem(this.storageKey, JSONStringify(queue));
|
|
2287
2256
|
return true;
|
|
2288
2257
|
} catch (err) {
|
|
2289
|
-
|
|
2258
|
+
this.reportError('Error saving queue', err);
|
|
2290
2259
|
return false;
|
|
2291
2260
|
}
|
|
2292
2261
|
};
|
|
@@ -2313,17 +2282,23 @@
|
|
|
2313
2282
|
* @constructor
|
|
2314
2283
|
*/
|
|
2315
2284
|
var RequestBatcher = function(storageKey, options) {
|
|
2316
|
-
this.
|
|
2285
|
+
this.errorReporter = options.errorReporter;
|
|
2286
|
+
this.queue = new RequestQueue(storageKey, {
|
|
2287
|
+
errorReporter: _.bind(this.reportError, this),
|
|
2288
|
+
storage: options.storage
|
|
2289
|
+
});
|
|
2317
2290
|
|
|
2318
2291
|
this.libConfig = options.libConfig;
|
|
2319
2292
|
this.sendRequest = options.sendRequestFunc;
|
|
2320
2293
|
this.beforeSendHook = options.beforeSendHook;
|
|
2294
|
+
this.stopAllBatching = options.stopAllBatchingFunc;
|
|
2321
2295
|
|
|
2322
2296
|
// seed variable batch size + flush interval with configured values
|
|
2323
2297
|
this.batchSize = this.libConfig['batch_size'];
|
|
2324
2298
|
this.flushInterval = this.libConfig['batch_flush_interval_ms'];
|
|
2325
2299
|
|
|
2326
2300
|
this.stopped = !this.libConfig['batch_autostart'];
|
|
2301
|
+
this.consecutiveRemovalFailures = 0;
|
|
2327
2302
|
};
|
|
2328
2303
|
|
|
2329
2304
|
/**
|
|
@@ -2339,6 +2314,7 @@
|
|
|
2339
2314
|
*/
|
|
2340
2315
|
RequestBatcher.prototype.start = function() {
|
|
2341
2316
|
this.stopped = false;
|
|
2317
|
+
this.consecutiveRemovalFailures = 0;
|
|
2342
2318
|
this.flush();
|
|
2343
2319
|
};
|
|
2344
2320
|
|
|
@@ -2443,14 +2419,14 @@
|
|
|
2443
2419
|
res.error === 'timeout' &&
|
|
2444
2420
|
new Date().getTime() - startTime >= timeoutMS
|
|
2445
2421
|
) {
|
|
2446
|
-
|
|
2422
|
+
this.reportError('Network timeout; retrying');
|
|
2447
2423
|
this.flush();
|
|
2448
2424
|
} else if (
|
|
2449
2425
|
_.isObject(res) &&
|
|
2450
2426
|
res.xhr_req &&
|
|
2451
|
-
(res.xhr_req['status'] >= 500 || res.xhr_req['status']
|
|
2427
|
+
(res.xhr_req['status'] >= 500 || res.xhr_req['status'] === 429 || res.error === 'timeout')
|
|
2452
2428
|
) {
|
|
2453
|
-
// network or API error, retry
|
|
2429
|
+
// network or API error, or 429 Too Many Requests, retry
|
|
2454
2430
|
var retryMS = this.flushInterval * 2;
|
|
2455
2431
|
var headers = res.xhr_req['responseHeaders'];
|
|
2456
2432
|
if (headers) {
|
|
@@ -2460,17 +2436,17 @@
|
|
|
2460
2436
|
}
|
|
2461
2437
|
}
|
|
2462
2438
|
retryMS = Math.min(MAX_RETRY_INTERVAL_MS, retryMS);
|
|
2463
|
-
|
|
2439
|
+
this.reportError('Error; retry in ' + retryMS + ' ms');
|
|
2464
2440
|
this.scheduleFlush(retryMS);
|
|
2465
2441
|
} else if (_.isObject(res) && res.xhr_req && res.xhr_req['status'] === 413) {
|
|
2466
2442
|
// 413 Payload Too Large
|
|
2467
2443
|
if (batch.length > 1) {
|
|
2468
2444
|
var halvedBatchSize = Math.max(1, Math.floor(currentBatchSize / 2));
|
|
2469
2445
|
this.batchSize = Math.min(this.batchSize, halvedBatchSize, batch.length - 1);
|
|
2470
|
-
|
|
2446
|
+
this.reportError('413 response; reducing batch size to ' + this.batchSize);
|
|
2471
2447
|
this.resetFlush();
|
|
2472
2448
|
} else {
|
|
2473
|
-
|
|
2449
|
+
this.reportError('Single-event request too large; dropping', batch);
|
|
2474
2450
|
this.resetBatchSize();
|
|
2475
2451
|
removeItemsFromQueue = true;
|
|
2476
2452
|
}
|
|
@@ -2483,12 +2459,25 @@
|
|
|
2483
2459
|
if (removeItemsFromQueue) {
|
|
2484
2460
|
this.queue.removeItemsByID(
|
|
2485
2461
|
_.map(batch, function(item) { return item['id']; }),
|
|
2486
|
-
_.bind(
|
|
2462
|
+
_.bind(function(succeeded) {
|
|
2463
|
+
if (succeeded) {
|
|
2464
|
+
this.consecutiveRemovalFailures = 0;
|
|
2465
|
+
this.flush(); // handle next batch if the queue isn't empty
|
|
2466
|
+
} else {
|
|
2467
|
+
this.reportError('Failed to remove items from queue');
|
|
2468
|
+
if (++this.consecutiveRemovalFailures > 5) {
|
|
2469
|
+
this.reportError('Too many queue failures; disabling batching system.');
|
|
2470
|
+
this.stopAllBatching();
|
|
2471
|
+
} else {
|
|
2472
|
+
this.resetFlush();
|
|
2473
|
+
}
|
|
2474
|
+
}
|
|
2475
|
+
}, this)
|
|
2487
2476
|
);
|
|
2488
2477
|
}
|
|
2489
2478
|
|
|
2490
2479
|
} catch(err) {
|
|
2491
|
-
|
|
2480
|
+
this.reportError('Error handling API response', err);
|
|
2492
2481
|
this.resetFlush();
|
|
2493
2482
|
}
|
|
2494
2483
|
}, this);
|
|
@@ -2505,11 +2494,28 @@
|
|
|
2505
2494
|
this.sendRequest(dataForRequest, requestOptions, batchSendCallback);
|
|
2506
2495
|
|
|
2507
2496
|
} catch(err) {
|
|
2508
|
-
|
|
2497
|
+
this.reportError('Error flushing request queue', err);
|
|
2509
2498
|
this.resetFlush();
|
|
2510
2499
|
}
|
|
2511
2500
|
};
|
|
2512
2501
|
|
|
2502
|
+
/**
|
|
2503
|
+
* Log error to global logger and optional user-defined logger.
|
|
2504
|
+
*/
|
|
2505
|
+
RequestBatcher.prototype.reportError = function(msg, err) {
|
|
2506
|
+
logger.error.apply(logger.error, arguments);
|
|
2507
|
+
if (this.errorReporter) {
|
|
2508
|
+
try {
|
|
2509
|
+
if (!(err instanceof Error)) {
|
|
2510
|
+
err = new Error(msg);
|
|
2511
|
+
}
|
|
2512
|
+
this.errorReporter(msg, err);
|
|
2513
|
+
} catch(err) {
|
|
2514
|
+
logger.error(err);
|
|
2515
|
+
}
|
|
2516
|
+
}
|
|
2517
|
+
};
|
|
2518
|
+
|
|
2513
2519
|
/**
|
|
2514
2520
|
* A function used to track a Mixpanel event (e.g. MixpanelLib.track)
|
|
2515
2521
|
* @callback trackFunction
|
|
@@ -3020,9 +3026,13 @@
|
|
|
3020
3026
|
* Permanently delete a group.
|
|
3021
3027
|
*
|
|
3022
3028
|
* ### Usage:
|
|
3029
|
+
*
|
|
3023
3030
|
* mixpanel.get_group('company', 'mixpanel').delete();
|
|
3031
|
+
*
|
|
3032
|
+
* @param {Function} [callback] If provided, the callback will be called after the tracking event
|
|
3024
3033
|
*/
|
|
3025
3034
|
MixpanelGroup.prototype['delete'] = addOptOutCheckMixpanelGroup(function(callback) {
|
|
3035
|
+
// bracket notation above prevents a minification error related to reserved words
|
|
3026
3036
|
var data = this.delete_action();
|
|
3027
3037
|
return this._send_request(data, callback);
|
|
3028
3038
|
});
|
|
@@ -3077,2755 +3087,929 @@
|
|
|
3077
3087
|
MixpanelGroup.prototype['unset'] = MixpanelGroup.prototype.unset;
|
|
3078
3088
|
MixpanelGroup.prototype['toString'] = MixpanelGroup.prototype.toString;
|
|
3079
3089
|
|
|
3080
|
-
/*
|
|
3081
|
-
* Constants
|
|
3082
|
-
*/
|
|
3083
|
-
/** @const */ var SET_QUEUE_KEY = '__mps';
|
|
3084
|
-
/** @const */ var SET_ONCE_QUEUE_KEY = '__mpso';
|
|
3085
|
-
/** @const */ var UNSET_QUEUE_KEY = '__mpus';
|
|
3086
|
-
/** @const */ var ADD_QUEUE_KEY = '__mpa';
|
|
3087
|
-
/** @const */ var APPEND_QUEUE_KEY = '__mpap';
|
|
3088
|
-
/** @const */ var REMOVE_QUEUE_KEY = '__mpr';
|
|
3089
|
-
/** @const */ var UNION_QUEUE_KEY = '__mpu';
|
|
3090
|
-
// This key is deprecated, but we want to check for it to see whether aliasing is allowed.
|
|
3091
|
-
/** @const */ var PEOPLE_DISTINCT_ID_KEY = '$people_distinct_id';
|
|
3092
|
-
/** @const */ var ALIAS_ID_KEY = '__alias';
|
|
3093
|
-
/** @const */ var CAMPAIGN_IDS_KEY = '__cmpns';
|
|
3094
|
-
/** @const */ var EVENT_TIMERS_KEY = '__timers';
|
|
3095
|
-
/** @const */ var RESERVED_PROPERTIES = [
|
|
3096
|
-
SET_QUEUE_KEY,
|
|
3097
|
-
SET_ONCE_QUEUE_KEY,
|
|
3098
|
-
UNSET_QUEUE_KEY,
|
|
3099
|
-
ADD_QUEUE_KEY,
|
|
3100
|
-
APPEND_QUEUE_KEY,
|
|
3101
|
-
REMOVE_QUEUE_KEY,
|
|
3102
|
-
UNION_QUEUE_KEY,
|
|
3103
|
-
PEOPLE_DISTINCT_ID_KEY,
|
|
3104
|
-
ALIAS_ID_KEY,
|
|
3105
|
-
CAMPAIGN_IDS_KEY,
|
|
3106
|
-
EVENT_TIMERS_KEY
|
|
3107
|
-
];
|
|
3108
|
-
|
|
3109
3090
|
/**
|
|
3110
|
-
* Mixpanel
|
|
3091
|
+
* Mixpanel People Object
|
|
3111
3092
|
* @constructor
|
|
3112
3093
|
*/
|
|
3113
|
-
var
|
|
3114
|
-
this['props'] = {};
|
|
3115
|
-
this.campaign_params_saved = false;
|
|
3116
|
-
|
|
3117
|
-
if (config['persistence_name']) {
|
|
3118
|
-
this.name = 'mp_' + config['persistence_name'];
|
|
3119
|
-
} else {
|
|
3120
|
-
this.name = 'mp_' + config['token'] + '_mixpanel';
|
|
3121
|
-
}
|
|
3122
|
-
|
|
3123
|
-
var storage_type = config['persistence'];
|
|
3124
|
-
if (storage_type !== 'cookie' && storage_type !== 'localStorage') {
|
|
3125
|
-
console.critical('Unknown persistence type ' + storage_type + '; falling back to cookie');
|
|
3126
|
-
storage_type = config['persistence'] = 'cookie';
|
|
3127
|
-
}
|
|
3128
|
-
|
|
3129
|
-
if (storage_type === 'localStorage' && _.localStorage.is_supported()) {
|
|
3130
|
-
this.storage = _.localStorage;
|
|
3131
|
-
} else {
|
|
3132
|
-
this.storage = _.cookie;
|
|
3133
|
-
}
|
|
3094
|
+
var MixpanelPeople = function() {};
|
|
3134
3095
|
|
|
3135
|
-
|
|
3136
|
-
this.update_config(config);
|
|
3137
|
-
this.upgrade(config);
|
|
3138
|
-
this.save();
|
|
3139
|
-
};
|
|
3096
|
+
_.extend(MixpanelPeople.prototype, apiActions);
|
|
3140
3097
|
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
// Filter out reserved properties
|
|
3144
|
-
_.each(this['props'], function(v, k) {
|
|
3145
|
-
if (!_.include(RESERVED_PROPERTIES, k)) {
|
|
3146
|
-
p[k] = v;
|
|
3147
|
-
}
|
|
3148
|
-
});
|
|
3149
|
-
return p;
|
|
3098
|
+
MixpanelPeople.prototype._init = function(mixpanel_instance) {
|
|
3099
|
+
this._mixpanel = mixpanel_instance;
|
|
3150
3100
|
};
|
|
3151
3101
|
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3102
|
+
/*
|
|
3103
|
+
* Set properties on a user record.
|
|
3104
|
+
*
|
|
3105
|
+
* ### Usage:
|
|
3106
|
+
*
|
|
3107
|
+
* mixpanel.people.set('gender', 'm');
|
|
3108
|
+
*
|
|
3109
|
+
* // or set multiple properties at once
|
|
3110
|
+
* mixpanel.people.set({
|
|
3111
|
+
* 'Company': 'Acme',
|
|
3112
|
+
* 'Plan': 'Premium',
|
|
3113
|
+
* 'Upgrade date': new Date()
|
|
3114
|
+
* });
|
|
3115
|
+
* // properties can be strings, integers, dates, or lists
|
|
3116
|
+
*
|
|
3117
|
+
* @param {Object|String} prop If a string, this is the name of the property. If an object, this is an associative array of names and values.
|
|
3118
|
+
* @param {*} [to] A value to set on the given property name
|
|
3119
|
+
* @param {Function} [callback] If provided, the callback will be called after tracking the event.
|
|
3120
|
+
*/
|
|
3121
|
+
MixpanelPeople.prototype.set = addOptOutCheckMixpanelPeople(function(prop, to, callback) {
|
|
3122
|
+
var data = this.set_action(prop, to);
|
|
3123
|
+
if (_.isObject(prop)) {
|
|
3124
|
+
callback = to;
|
|
3159
3125
|
}
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
var upgrade_from_old_lib = config['upgrade'],
|
|
3164
|
-
old_cookie_name,
|
|
3165
|
-
old_cookie;
|
|
3166
|
-
|
|
3167
|
-
if (upgrade_from_old_lib) {
|
|
3168
|
-
old_cookie_name = 'mp_super_properties';
|
|
3169
|
-
// Case where they had a custom cookie name before.
|
|
3170
|
-
if (typeof(upgrade_from_old_lib) === 'string') {
|
|
3171
|
-
old_cookie_name = upgrade_from_old_lib;
|
|
3172
|
-
}
|
|
3173
|
-
|
|
3174
|
-
old_cookie = this.storage.parse(old_cookie_name);
|
|
3175
|
-
|
|
3176
|
-
// remove the cookie
|
|
3177
|
-
this.storage.remove(old_cookie_name);
|
|
3178
|
-
this.storage.remove(old_cookie_name, true);
|
|
3179
|
-
|
|
3180
|
-
if (old_cookie) {
|
|
3181
|
-
this['props'] = _.extend(
|
|
3182
|
-
this['props'],
|
|
3183
|
-
old_cookie['all'],
|
|
3184
|
-
old_cookie['events']
|
|
3185
|
-
);
|
|
3186
|
-
}
|
|
3126
|
+
// make sure that the referrer info has been updated and saved
|
|
3127
|
+
if (this._get_config('save_referrer')) {
|
|
3128
|
+
this._mixpanel['persistence'].update_referrer_info(document.referrer);
|
|
3187
3129
|
}
|
|
3188
3130
|
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3131
|
+
// update $set object with default people properties
|
|
3132
|
+
data[SET_ACTION] = _.extend(
|
|
3133
|
+
{},
|
|
3134
|
+
_.info.people_properties(),
|
|
3135
|
+
this._mixpanel['persistence'].get_referrer_info(),
|
|
3136
|
+
data[SET_ACTION]
|
|
3137
|
+
);
|
|
3138
|
+
return this._send_request(data, callback);
|
|
3139
|
+
});
|
|
3198
3140
|
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3141
|
+
/*
|
|
3142
|
+
* Set properties on a user record, only if they do not yet exist.
|
|
3143
|
+
* This will not overwrite previous people property values, unlike
|
|
3144
|
+
* people.set().
|
|
3145
|
+
*
|
|
3146
|
+
* ### Usage:
|
|
3147
|
+
*
|
|
3148
|
+
* mixpanel.people.set_once('First Login Date', new Date());
|
|
3149
|
+
*
|
|
3150
|
+
* // or set multiple properties at once
|
|
3151
|
+
* mixpanel.people.set_once({
|
|
3152
|
+
* 'First Login Date': new Date(),
|
|
3153
|
+
* 'Starting Plan': 'Premium'
|
|
3154
|
+
* });
|
|
3155
|
+
*
|
|
3156
|
+
* // properties can be strings, integers or dates
|
|
3157
|
+
*
|
|
3158
|
+
* @param {Object|String} prop If a string, this is the name of the property. If an object, this is an associative array of names and values.
|
|
3159
|
+
* @param {*} [to] A value to set on the given property name
|
|
3160
|
+
* @param {Function} [callback] If provided, the callback will be called after tracking the event.
|
|
3161
|
+
*/
|
|
3162
|
+
MixpanelPeople.prototype.set_once = addOptOutCheckMixpanelPeople(function(prop, to, callback) {
|
|
3163
|
+
var data = this.set_once_action(prop, to);
|
|
3164
|
+
if (_.isObject(prop)) {
|
|
3165
|
+
callback = to;
|
|
3203
3166
|
}
|
|
3167
|
+
return this._send_request(data, callback);
|
|
3168
|
+
});
|
|
3204
3169
|
|
|
3205
|
-
|
|
3206
|
-
|
|
3170
|
+
/*
|
|
3171
|
+
* Unset properties on a user record (permanently removes the properties and their values from a profile).
|
|
3172
|
+
*
|
|
3173
|
+
* ### Usage:
|
|
3174
|
+
*
|
|
3175
|
+
* mixpanel.people.unset('gender');
|
|
3176
|
+
*
|
|
3177
|
+
* // or unset multiple properties at once
|
|
3178
|
+
* mixpanel.people.unset(['gender', 'Company']);
|
|
3179
|
+
*
|
|
3180
|
+
* @param {Array|String} prop If a string, this is the name of the property. If an array, this is a list of property names.
|
|
3181
|
+
* @param {Function} [callback] If provided, the callback will be called after tracking the event.
|
|
3182
|
+
*/
|
|
3183
|
+
MixpanelPeople.prototype.unset = addOptOutCheckMixpanelPeople(function(prop, callback) {
|
|
3184
|
+
var data = this.unset_action(prop);
|
|
3185
|
+
return this._send_request(data, callback);
|
|
3186
|
+
});
|
|
3207
3187
|
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
}
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
// remove both domain and subdomain cookies
|
|
3233
|
-
this.storage.remove(this.name, false, this.cookie_domain);
|
|
3234
|
-
this.storage.remove(this.name, true, this.cookie_domain);
|
|
3235
|
-
};
|
|
3236
|
-
|
|
3237
|
-
// removes the storage entry and deletes all loaded data
|
|
3238
|
-
// forced name for tests
|
|
3239
|
-
MixpanelPersistence.prototype.clear = function() {
|
|
3240
|
-
this.remove();
|
|
3241
|
-
this['props'] = {};
|
|
3242
|
-
};
|
|
3243
|
-
|
|
3244
|
-
/**
|
|
3245
|
-
* @param {Object} props
|
|
3246
|
-
* @param {*=} default_value
|
|
3247
|
-
* @param {number=} days
|
|
3188
|
+
/*
|
|
3189
|
+
* Increment/decrement numeric people analytics properties.
|
|
3190
|
+
*
|
|
3191
|
+
* ### Usage:
|
|
3192
|
+
*
|
|
3193
|
+
* mixpanel.people.increment('page_views', 1);
|
|
3194
|
+
*
|
|
3195
|
+
* // or, for convenience, if you're just incrementing a counter by
|
|
3196
|
+
* // 1, you can simply do
|
|
3197
|
+
* mixpanel.people.increment('page_views');
|
|
3198
|
+
*
|
|
3199
|
+
* // to decrement a counter, pass a negative number
|
|
3200
|
+
* mixpanel.people.increment('credits_left', -1);
|
|
3201
|
+
*
|
|
3202
|
+
* // like mixpanel.people.set(), you can increment multiple
|
|
3203
|
+
* // properties at once:
|
|
3204
|
+
* mixpanel.people.increment({
|
|
3205
|
+
* counter1: 1,
|
|
3206
|
+
* counter2: 6
|
|
3207
|
+
* });
|
|
3208
|
+
*
|
|
3209
|
+
* @param {Object|String} prop If a string, this is the name of the property. If an object, this is an associative array of names and numeric values.
|
|
3210
|
+
* @param {Number} [by] An amount to increment the given property
|
|
3211
|
+
* @param {Function} [callback] If provided, the callback will be called after tracking the event.
|
|
3248
3212
|
*/
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3213
|
+
MixpanelPeople.prototype.increment = addOptOutCheckMixpanelPeople(function(prop, by, callback) {
|
|
3214
|
+
var data = {};
|
|
3215
|
+
var $add = {};
|
|
3216
|
+
if (_.isObject(prop)) {
|
|
3217
|
+
_.each(prop, function(v, k) {
|
|
3218
|
+
if (!this._is_reserved_property(k)) {
|
|
3219
|
+
if (isNaN(parseFloat(v))) {
|
|
3220
|
+
console.error('Invalid increment value passed to mixpanel.people.increment - must be a number');
|
|
3221
|
+
return;
|
|
3222
|
+
} else {
|
|
3223
|
+
$add[k] = v;
|
|
3224
|
+
}
|
|
3257
3225
|
}
|
|
3258
3226
|
}, this);
|
|
3227
|
+
callback = by;
|
|
3228
|
+
} else {
|
|
3229
|
+
// convenience: mixpanel.people.increment('property'); will
|
|
3230
|
+
// increment 'property' by 1
|
|
3231
|
+
if (_.isUndefined(by)) {
|
|
3232
|
+
by = 1;
|
|
3233
|
+
}
|
|
3234
|
+
$add[prop] = by;
|
|
3235
|
+
}
|
|
3236
|
+
data[ADD_ACTION] = $add;
|
|
3259
3237
|
|
|
3260
|
-
|
|
3238
|
+
return this._send_request(data, callback);
|
|
3239
|
+
});
|
|
3261
3240
|
|
|
3262
|
-
|
|
3241
|
+
/*
|
|
3242
|
+
* Append a value to a list-valued people analytics property.
|
|
3243
|
+
*
|
|
3244
|
+
* ### Usage:
|
|
3245
|
+
*
|
|
3246
|
+
* // append a value to a list, creating it if needed
|
|
3247
|
+
* mixpanel.people.append('pages_visited', 'homepage');
|
|
3248
|
+
*
|
|
3249
|
+
* // like mixpanel.people.set(), you can append multiple
|
|
3250
|
+
* // properties at once:
|
|
3251
|
+
* mixpanel.people.append({
|
|
3252
|
+
* list1: 'bob',
|
|
3253
|
+
* list2: 123
|
|
3254
|
+
* });
|
|
3255
|
+
*
|
|
3256
|
+
* @param {Object|String} list_name If a string, this is the name of the property. If an object, this is an associative array of names and values.
|
|
3257
|
+
* @param {*} [value] value An item to append to the list
|
|
3258
|
+
* @param {Function} [callback] If provided, the callback will be called after tracking the event.
|
|
3259
|
+
*/
|
|
3260
|
+
MixpanelPeople.prototype.append = addOptOutCheckMixpanelPeople(function(list_name, value, callback) {
|
|
3261
|
+
if (_.isObject(list_name)) {
|
|
3262
|
+
callback = value;
|
|
3263
3263
|
}
|
|
3264
|
-
|
|
3265
|
-
|
|
3264
|
+
var data = this.append_action(list_name, value);
|
|
3265
|
+
return this._send_request(data, callback);
|
|
3266
|
+
});
|
|
3266
3267
|
|
|
3267
|
-
|
|
3268
|
-
*
|
|
3269
|
-
*
|
|
3268
|
+
/*
|
|
3269
|
+
* Remove a value from a list-valued people analytics property.
|
|
3270
|
+
*
|
|
3271
|
+
* ### Usage:
|
|
3272
|
+
*
|
|
3273
|
+
* mixpanel.people.remove('School', 'UCB');
|
|
3274
|
+
*
|
|
3275
|
+
* @param {Object|String} list_name If a string, this is the name of the property. If an object, this is an associative array of names and values.
|
|
3276
|
+
* @param {*} [value] value Item to remove from the list
|
|
3277
|
+
* @param {Function} [callback] If provided, the callback will be called after tracking the event.
|
|
3270
3278
|
*/
|
|
3271
|
-
|
|
3272
|
-
if (_.isObject(
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
_.extend(this['props'], props);
|
|
3276
|
-
|
|
3277
|
-
this.save();
|
|
3278
|
-
|
|
3279
|
-
return true;
|
|
3279
|
+
MixpanelPeople.prototype.remove = addOptOutCheckMixpanelPeople(function(list_name, value, callback) {
|
|
3280
|
+
if (_.isObject(list_name)) {
|
|
3281
|
+
callback = value;
|
|
3280
3282
|
}
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
+
var data = this.remove_action(list_name, value);
|
|
3284
|
+
return this._send_request(data, callback);
|
|
3285
|
+
});
|
|
3283
3286
|
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3287
|
+
/*
|
|
3288
|
+
* Merge a given list with a list-valued people analytics property,
|
|
3289
|
+
* excluding duplicate values.
|
|
3290
|
+
*
|
|
3291
|
+
* ### Usage:
|
|
3292
|
+
*
|
|
3293
|
+
* // merge a value to a list, creating it if needed
|
|
3294
|
+
* mixpanel.people.union('pages_visited', 'homepage');
|
|
3295
|
+
*
|
|
3296
|
+
* // like mixpanel.people.set(), you can append multiple
|
|
3297
|
+
* // properties at once:
|
|
3298
|
+
* mixpanel.people.union({
|
|
3299
|
+
* list1: 'bob',
|
|
3300
|
+
* list2: 123
|
|
3301
|
+
* });
|
|
3302
|
+
*
|
|
3303
|
+
* // like mixpanel.people.append(), you can append multiple
|
|
3304
|
+
* // values to the same list:
|
|
3305
|
+
* mixpanel.people.union({
|
|
3306
|
+
* list1: ['bob', 'billy']
|
|
3307
|
+
* });
|
|
3308
|
+
*
|
|
3309
|
+
* @param {Object|String} list_name If a string, this is the name of the property. If an object, this is an associative array of names and values.
|
|
3310
|
+
* @param {*} [value] Value / values to merge with the given property
|
|
3311
|
+
* @param {Function} [callback] If provided, the callback will be called after tracking the event.
|
|
3312
|
+
*/
|
|
3313
|
+
MixpanelPeople.prototype.union = addOptOutCheckMixpanelPeople(function(list_name, values, callback) {
|
|
3314
|
+
if (_.isObject(list_name)) {
|
|
3315
|
+
callback = values;
|
|
3288
3316
|
}
|
|
3289
|
-
|
|
3317
|
+
var data = this.union_action(list_name, values);
|
|
3318
|
+
return this._send_request(data, callback);
|
|
3319
|
+
});
|
|
3290
3320
|
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3321
|
+
/*
|
|
3322
|
+
* Record that you have charged the current user a certain amount
|
|
3323
|
+
* of money. Charges recorded with track_charge() will appear in the
|
|
3324
|
+
* Mixpanel revenue report.
|
|
3325
|
+
*
|
|
3326
|
+
* ### Usage:
|
|
3327
|
+
*
|
|
3328
|
+
* // charge a user $50
|
|
3329
|
+
* mixpanel.people.track_charge(50);
|
|
3330
|
+
*
|
|
3331
|
+
* // charge a user $30.50 on the 2nd of january
|
|
3332
|
+
* mixpanel.people.track_charge(30.50, {
|
|
3333
|
+
* '$time': new Date('jan 1 2012')
|
|
3334
|
+
* });
|
|
3335
|
+
*
|
|
3336
|
+
* @param {Number} amount The amount of money charged to the current user
|
|
3337
|
+
* @param {Object} [properties] An associative array of properties associated with the charge
|
|
3338
|
+
* @param {Function} [callback] If provided, the callback will be called when the server responds
|
|
3339
|
+
*/
|
|
3340
|
+
MixpanelPeople.prototype.track_charge = addOptOutCheckMixpanelPeople(function(amount, properties, callback) {
|
|
3341
|
+
if (!_.isNumber(amount)) {
|
|
3342
|
+
amount = parseFloat(amount);
|
|
3343
|
+
if (isNaN(amount)) {
|
|
3344
|
+
console.error('Invalid value passed to mixpanel.people.track_charge - must be a number');
|
|
3345
|
+
return;
|
|
3300
3346
|
}
|
|
3301
3347
|
}
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3348
|
+
|
|
3349
|
+
return this.append('$transactions', _.extend({
|
|
3350
|
+
'$amount': amount
|
|
3351
|
+
}, properties), callback);
|
|
3305
3352
|
});
|
|
3306
3353
|
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3354
|
+
/*
|
|
3355
|
+
* Permanently clear all revenue report transactions from the
|
|
3356
|
+
* current user's people analytics profile.
|
|
3357
|
+
*
|
|
3358
|
+
* ### Usage:
|
|
3359
|
+
*
|
|
3360
|
+
* mixpanel.people.clear_charges();
|
|
3361
|
+
*
|
|
3362
|
+
* @param {Function} [callback] If provided, the callback will be called after tracking the event.
|
|
3363
|
+
*/
|
|
3364
|
+
MixpanelPeople.prototype.clear_charges = function(callback) {
|
|
3365
|
+
return this.set('$transactions', [], callback);
|
|
3366
|
+
};
|
|
3367
|
+
|
|
3368
|
+
/*
|
|
3369
|
+
* Permanently deletes the current people analytics profile from
|
|
3370
|
+
* Mixpanel (using the current distinct_id).
|
|
3371
|
+
*
|
|
3372
|
+
* ### Usage:
|
|
3373
|
+
*
|
|
3374
|
+
* // remove the all data you have stored about the current user
|
|
3375
|
+
* mixpanel.people.delete_user();
|
|
3376
|
+
*
|
|
3377
|
+
*/
|
|
3378
|
+
MixpanelPeople.prototype.delete_user = function() {
|
|
3379
|
+
if (!this._identify_called()) {
|
|
3380
|
+
console.error('mixpanel.people.delete_user() requires you to call identify() first');
|
|
3381
|
+
return;
|
|
3311
3382
|
}
|
|
3383
|
+
var data = {'$delete': this._mixpanel.get_distinct_id()};
|
|
3384
|
+
return this._send_request(data);
|
|
3312
3385
|
};
|
|
3313
3386
|
|
|
3314
|
-
|
|
3315
|
-
this.
|
|
3387
|
+
MixpanelPeople.prototype.toString = function() {
|
|
3388
|
+
return this._mixpanel.toString() + '.people';
|
|
3316
3389
|
};
|
|
3317
3390
|
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
this.
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3391
|
+
MixpanelPeople.prototype._send_request = function(data, callback) {
|
|
3392
|
+
data['$token'] = this._get_config('token');
|
|
3393
|
+
data['$distinct_id'] = this._mixpanel.get_distinct_id();
|
|
3394
|
+
var device_id = this._mixpanel.get_property('$device_id');
|
|
3395
|
+
var user_id = this._mixpanel.get_property('$user_id');
|
|
3396
|
+
var had_persisted_distinct_id = this._mixpanel.get_property('$had_persisted_distinct_id');
|
|
3397
|
+
if (device_id) {
|
|
3398
|
+
data['$device_id'] = device_id;
|
|
3399
|
+
}
|
|
3400
|
+
if (user_id) {
|
|
3401
|
+
data['$user_id'] = user_id;
|
|
3402
|
+
}
|
|
3403
|
+
if (had_persisted_distinct_id) {
|
|
3404
|
+
data['$had_persisted_distinct_id'] = had_persisted_distinct_id;
|
|
3405
|
+
}
|
|
3326
3406
|
|
|
3327
|
-
|
|
3328
|
-
return _.strip_empty_properties({
|
|
3329
|
-
'$initial_referrer': this['props']['$initial_referrer'],
|
|
3330
|
-
'$initial_referring_domain': this['props']['$initial_referring_domain']
|
|
3331
|
-
});
|
|
3332
|
-
};
|
|
3407
|
+
var date_encoded_data = _.encodeDates(data);
|
|
3333
3408
|
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3409
|
+
if (!this._identify_called()) {
|
|
3410
|
+
this._enqueue(data);
|
|
3411
|
+
if (!_.isUndefined(callback)) {
|
|
3412
|
+
if (this._get_config('verbose')) {
|
|
3413
|
+
callback({status: -1, error: null});
|
|
3414
|
+
} else {
|
|
3415
|
+
callback(-1);
|
|
3416
|
+
}
|
|
3341
3417
|
}
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
return props;
|
|
3345
|
-
};
|
|
3346
|
-
|
|
3347
|
-
MixpanelPersistence.prototype.update_config = function(config) {
|
|
3348
|
-
this.default_expiry = this.expire_days = config['cookie_expiration'];
|
|
3349
|
-
this.set_disabled(config['disable_persistence']);
|
|
3350
|
-
this.set_cookie_domain(config['cookie_domain']);
|
|
3351
|
-
this.set_cross_site(config['cross_site_cookie']);
|
|
3352
|
-
this.set_cross_subdomain(config['cross_subdomain_cookie']);
|
|
3353
|
-
this.set_secure(config['secure_cookie']);
|
|
3354
|
-
};
|
|
3355
|
-
|
|
3356
|
-
MixpanelPersistence.prototype.set_disabled = function(disabled) {
|
|
3357
|
-
this.disabled = disabled;
|
|
3358
|
-
if (this.disabled) {
|
|
3359
|
-
this.remove();
|
|
3360
|
-
} else {
|
|
3361
|
-
this.save();
|
|
3362
|
-
}
|
|
3363
|
-
};
|
|
3364
|
-
|
|
3365
|
-
MixpanelPersistence.prototype.set_cookie_domain = function(cookie_domain) {
|
|
3366
|
-
if (cookie_domain !== this.cookie_domain) {
|
|
3367
|
-
this.remove();
|
|
3368
|
-
this.cookie_domain = cookie_domain;
|
|
3369
|
-
this.save();
|
|
3418
|
+
return _.truncate(date_encoded_data, 255);
|
|
3370
3419
|
}
|
|
3371
|
-
};
|
|
3372
3420
|
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
this.
|
|
3377
|
-
this.
|
|
3378
|
-
}
|
|
3421
|
+
return this._mixpanel._track_or_batch({
|
|
3422
|
+
type: 'people',
|
|
3423
|
+
data: date_encoded_data,
|
|
3424
|
+
endpoint: this._get_config('api_host') + '/engage/',
|
|
3425
|
+
batcher: this._mixpanel.request_batchers.people
|
|
3426
|
+
}, callback);
|
|
3379
3427
|
};
|
|
3380
3428
|
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
this.cross_subdomain = cross_subdomain;
|
|
3384
|
-
this.remove();
|
|
3385
|
-
this.save();
|
|
3386
|
-
}
|
|
3429
|
+
MixpanelPeople.prototype._get_config = function(conf_var) {
|
|
3430
|
+
return this._mixpanel.get_config(conf_var);
|
|
3387
3431
|
};
|
|
3388
3432
|
|
|
3389
|
-
|
|
3390
|
-
return this.
|
|
3433
|
+
MixpanelPeople.prototype._identify_called = function() {
|
|
3434
|
+
return this._mixpanel._flags.identify_called === true;
|
|
3391
3435
|
};
|
|
3392
3436
|
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
this.
|
|
3397
|
-
|
|
3437
|
+
// Queue up engage operations if identify hasn't been called yet.
|
|
3438
|
+
MixpanelPeople.prototype._enqueue = function(data) {
|
|
3439
|
+
if (SET_ACTION in data) {
|
|
3440
|
+
this._mixpanel['persistence']._add_to_people_queue(SET_ACTION, data);
|
|
3441
|
+
} else if (SET_ONCE_ACTION in data) {
|
|
3442
|
+
this._mixpanel['persistence']._add_to_people_queue(SET_ONCE_ACTION, data);
|
|
3443
|
+
} else if (UNSET_ACTION in data) {
|
|
3444
|
+
this._mixpanel['persistence']._add_to_people_queue(UNSET_ACTION, data);
|
|
3445
|
+
} else if (ADD_ACTION in data) {
|
|
3446
|
+
this._mixpanel['persistence']._add_to_people_queue(ADD_ACTION, data);
|
|
3447
|
+
} else if (APPEND_ACTION in data) {
|
|
3448
|
+
this._mixpanel['persistence']._add_to_people_queue(APPEND_ACTION, data);
|
|
3449
|
+
} else if (REMOVE_ACTION in data) {
|
|
3450
|
+
this._mixpanel['persistence']._add_to_people_queue(REMOVE_ACTION, data);
|
|
3451
|
+
} else if (UNION_ACTION in data) {
|
|
3452
|
+
this._mixpanel['persistence']._add_to_people_queue(UNION_ACTION, data);
|
|
3453
|
+
} else {
|
|
3454
|
+
console.error('Invalid call to _enqueue():', data);
|
|
3398
3455
|
}
|
|
3399
3456
|
};
|
|
3400
3457
|
|
|
3401
|
-
|
|
3402
|
-
var
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
set_once_q = this._get_or_create_queue(SET_ONCE_ACTION),
|
|
3406
|
-
unset_q = this._get_or_create_queue(UNSET_ACTION),
|
|
3407
|
-
add_q = this._get_or_create_queue(ADD_ACTION),
|
|
3408
|
-
union_q = this._get_or_create_queue(UNION_ACTION),
|
|
3409
|
-
remove_q = this._get_or_create_queue(REMOVE_ACTION, []),
|
|
3410
|
-
append_q = this._get_or_create_queue(APPEND_ACTION, []);
|
|
3458
|
+
MixpanelPeople.prototype._flush_one_queue = function(action, action_method, callback, queue_to_params_fn) {
|
|
3459
|
+
var _this = this;
|
|
3460
|
+
var queued_data = _.extend({}, this._mixpanel['persistence']._get_queue(action));
|
|
3461
|
+
var action_params = queued_data;
|
|
3411
3462
|
|
|
3412
|
-
if (
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
_.each(q_data, function(v, k) {
|
|
3425
|
-
if (!(k in set_once_q)) {
|
|
3426
|
-
set_once_q[k] = v;
|
|
3463
|
+
if (!_.isUndefined(queued_data) && _.isObject(queued_data) && !_.isEmptyObject(queued_data)) {
|
|
3464
|
+
_this._mixpanel['persistence']._pop_from_people_queue(action, queued_data);
|
|
3465
|
+
if (queue_to_params_fn) {
|
|
3466
|
+
action_params = queue_to_params_fn(queued_data);
|
|
3467
|
+
}
|
|
3468
|
+
action_method.call(_this, action_params, function(response, data) {
|
|
3469
|
+
// on bad response, we want to add it back to the queue
|
|
3470
|
+
if (response === 0) {
|
|
3471
|
+
_this._mixpanel['persistence']._add_to_people_queue(action, queued_data);
|
|
3472
|
+
}
|
|
3473
|
+
if (!_.isUndefined(callback)) {
|
|
3474
|
+
callback(response, data);
|
|
3427
3475
|
}
|
|
3428
3476
|
});
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
_.each(q_data, function(prop) {
|
|
3477
|
+
}
|
|
3478
|
+
};
|
|
3432
3479
|
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
delete append_obj[prop];
|
|
3442
|
-
}
|
|
3443
|
-
});
|
|
3480
|
+
// Flush queued engage operations - order does not matter,
|
|
3481
|
+
// and there are network level race conditions anyway
|
|
3482
|
+
MixpanelPeople.prototype._flush = function(
|
|
3483
|
+
_set_callback, _add_callback, _append_callback, _set_once_callback, _union_callback, _unset_callback, _remove_callback
|
|
3484
|
+
) {
|
|
3485
|
+
var _this = this;
|
|
3486
|
+
var $append_queue = this._mixpanel['persistence']._get_queue(APPEND_ACTION);
|
|
3487
|
+
var $remove_queue = this._mixpanel['persistence']._get_queue(REMOVE_ACTION);
|
|
3444
3488
|
|
|
3445
|
-
|
|
3489
|
+
this._flush_one_queue(SET_ACTION, this.set, _set_callback);
|
|
3490
|
+
this._flush_one_queue(SET_ONCE_ACTION, this.set_once, _set_once_callback);
|
|
3491
|
+
this._flush_one_queue(UNSET_ACTION, this.unset, _unset_callback, function(queue) { return _.keys(queue); });
|
|
3492
|
+
this._flush_one_queue(ADD_ACTION, this.increment, _add_callback);
|
|
3493
|
+
this._flush_one_queue(UNION_ACTION, this.union, _union_callback);
|
|
3446
3494
|
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
if (
|
|
3453
|
-
|
|
3454
|
-
} else {
|
|
3455
|
-
// If it doesn't exist, update the add
|
|
3456
|
-
// queue
|
|
3457
|
-
if (!(k in add_q)) {
|
|
3458
|
-
add_q[k] = 0;
|
|
3459
|
-
}
|
|
3460
|
-
add_q[k] += v;
|
|
3495
|
+
// we have to fire off each $append individually since there is
|
|
3496
|
+
// no concat method server side
|
|
3497
|
+
if (!_.isUndefined($append_queue) && _.isArray($append_queue) && $append_queue.length) {
|
|
3498
|
+
var $append_item;
|
|
3499
|
+
var append_callback = function(response, data) {
|
|
3500
|
+
if (response === 0) {
|
|
3501
|
+
_this._mixpanel['persistence']._add_to_people_queue(APPEND_ACTION, $append_item);
|
|
3461
3502
|
}
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
} else if (q_key === UNION_QUEUE_KEY) {
|
|
3465
|
-
_.each(q_data, function(v, k) {
|
|
3466
|
-
if (_.isArray(v)) {
|
|
3467
|
-
if (!(k in union_q)) {
|
|
3468
|
-
union_q[k] = [];
|
|
3469
|
-
}
|
|
3470
|
-
// We may send duplicates, the server will dedup them.
|
|
3471
|
-
union_q[k] = union_q[k].concat(v);
|
|
3503
|
+
if (!_.isUndefined(_append_callback)) {
|
|
3504
|
+
_append_callback(response, data);
|
|
3472
3505
|
}
|
|
3473
|
-
}
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3506
|
+
};
|
|
3507
|
+
for (var i = $append_queue.length - 1; i >= 0; i--) {
|
|
3508
|
+
$append_item = $append_queue.pop();
|
|
3509
|
+
if (!_.isEmptyObject($append_item)) {
|
|
3510
|
+
_this.append($append_item, append_callback);
|
|
3511
|
+
}
|
|
3512
|
+
}
|
|
3513
|
+
// Save the shortened append queue
|
|
3514
|
+
_this._mixpanel['persistence'].save();
|
|
3481
3515
|
}
|
|
3482
3516
|
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
MixpanelPersistence.prototype._pop_from_people_queue = function(queue, data) {
|
|
3490
|
-
var q = this._get_queue(queue);
|
|
3491
|
-
if (!_.isUndefined(q)) {
|
|
3492
|
-
_.each(data, function(v, k) {
|
|
3493
|
-
if (queue === APPEND_ACTION || queue === REMOVE_ACTION) {
|
|
3494
|
-
// list actions: only remove if both k+v match
|
|
3495
|
-
// e.g. remove should not override append in a case like
|
|
3496
|
-
// append({foo: 'bar'}); remove({foo: 'qux'})
|
|
3497
|
-
_.each(q, function(queued_action) {
|
|
3498
|
-
if (queued_action[k] === v) {
|
|
3499
|
-
delete queued_action[k];
|
|
3500
|
-
}
|
|
3501
|
-
});
|
|
3502
|
-
} else {
|
|
3503
|
-
delete q[k];
|
|
3517
|
+
// same for $remove
|
|
3518
|
+
if (!_.isUndefined($remove_queue) && _.isArray($remove_queue) && $remove_queue.length) {
|
|
3519
|
+
var $remove_item;
|
|
3520
|
+
var remove_callback = function(response, data) {
|
|
3521
|
+
if (response === 0) {
|
|
3522
|
+
_this._mixpanel['persistence']._add_to_people_queue(REMOVE_ACTION, $remove_item);
|
|
3504
3523
|
}
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
} else if (queue === UNSET_ACTION) {
|
|
3517
|
-
return UNSET_QUEUE_KEY;
|
|
3518
|
-
} else if (queue === ADD_ACTION) {
|
|
3519
|
-
return ADD_QUEUE_KEY;
|
|
3520
|
-
} else if (queue === APPEND_ACTION) {
|
|
3521
|
-
return APPEND_QUEUE_KEY;
|
|
3522
|
-
} else if (queue === REMOVE_ACTION) {
|
|
3523
|
-
return REMOVE_QUEUE_KEY;
|
|
3524
|
-
} else if (queue === UNION_ACTION) {
|
|
3525
|
-
return UNION_QUEUE_KEY;
|
|
3526
|
-
} else {
|
|
3527
|
-
console.error('Invalid queue:', queue);
|
|
3524
|
+
if (!_.isUndefined(_remove_callback)) {
|
|
3525
|
+
_remove_callback(response, data);
|
|
3526
|
+
}
|
|
3527
|
+
};
|
|
3528
|
+
for (var j = $remove_queue.length - 1; j >= 0; j--) {
|
|
3529
|
+
$remove_item = $remove_queue.pop();
|
|
3530
|
+
if (!_.isEmptyObject($remove_item)) {
|
|
3531
|
+
_this.remove($remove_item, remove_callback);
|
|
3532
|
+
}
|
|
3533
|
+
}
|
|
3534
|
+
_this._mixpanel['persistence'].save();
|
|
3528
3535
|
}
|
|
3529
3536
|
};
|
|
3530
3537
|
|
|
3531
|
-
|
|
3532
|
-
return
|
|
3533
|
-
};
|
|
3534
|
-
MixpanelPersistence.prototype._get_or_create_queue = function(queue, default_val) {
|
|
3535
|
-
var key = this._get_queue_key(queue);
|
|
3536
|
-
default_val = _.isUndefined(default_val) ? {} : default_val;
|
|
3537
|
-
|
|
3538
|
-
return this['props'][key] || (this['props'][key] = default_val);
|
|
3539
|
-
};
|
|
3540
|
-
|
|
3541
|
-
MixpanelPersistence.prototype.set_event_timer = function(event_name, timestamp) {
|
|
3542
|
-
var timers = this['props'][EVENT_TIMERS_KEY] || {};
|
|
3543
|
-
timers[event_name] = timestamp;
|
|
3544
|
-
this['props'][EVENT_TIMERS_KEY] = timers;
|
|
3545
|
-
this.save();
|
|
3538
|
+
MixpanelPeople.prototype._is_reserved_property = function(prop) {
|
|
3539
|
+
return prop === '$distinct_id' || prop === '$token' || prop === '$device_id' || prop === '$user_id' || prop === '$had_persisted_distinct_id';
|
|
3546
3540
|
};
|
|
3547
3541
|
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3542
|
+
// MixpanelPeople Exports
|
|
3543
|
+
MixpanelPeople.prototype['set'] = MixpanelPeople.prototype.set;
|
|
3544
|
+
MixpanelPeople.prototype['set_once'] = MixpanelPeople.prototype.set_once;
|
|
3545
|
+
MixpanelPeople.prototype['unset'] = MixpanelPeople.prototype.unset;
|
|
3546
|
+
MixpanelPeople.prototype['increment'] = MixpanelPeople.prototype.increment;
|
|
3547
|
+
MixpanelPeople.prototype['append'] = MixpanelPeople.prototype.append;
|
|
3548
|
+
MixpanelPeople.prototype['remove'] = MixpanelPeople.prototype.remove;
|
|
3549
|
+
MixpanelPeople.prototype['union'] = MixpanelPeople.prototype.union;
|
|
3550
|
+
MixpanelPeople.prototype['track_charge'] = MixpanelPeople.prototype.track_charge;
|
|
3551
|
+
MixpanelPeople.prototype['clear_charges'] = MixpanelPeople.prototype.clear_charges;
|
|
3552
|
+
MixpanelPeople.prototype['delete_user'] = MixpanelPeople.prototype.delete_user;
|
|
3553
|
+
MixpanelPeople.prototype['toString'] = MixpanelPeople.prototype.toString;
|
|
3557
3554
|
|
|
3558
3555
|
/*
|
|
3559
|
-
*
|
|
3556
|
+
* Constants
|
|
3560
3557
|
*/
|
|
3558
|
+
/** @const */ var SET_QUEUE_KEY = '__mps';
|
|
3559
|
+
/** @const */ var SET_ONCE_QUEUE_KEY = '__mpso';
|
|
3560
|
+
/** @const */ var UNSET_QUEUE_KEY = '__mpus';
|
|
3561
|
+
/** @const */ var ADD_QUEUE_KEY = '__mpa';
|
|
3562
|
+
/** @const */ var APPEND_QUEUE_KEY = '__mpap';
|
|
3563
|
+
/** @const */ var REMOVE_QUEUE_KEY = '__mpr';
|
|
3564
|
+
/** @const */ var UNION_QUEUE_KEY = '__mpu';
|
|
3565
|
+
// This key is deprecated, but we want to check for it to see whether aliasing is allowed.
|
|
3566
|
+
/** @const */ var PEOPLE_DISTINCT_ID_KEY = '$people_distinct_id';
|
|
3567
|
+
/** @const */ var ALIAS_ID_KEY = '__alias';
|
|
3568
|
+
/** @const */ var EVENT_TIMERS_KEY = '__timers';
|
|
3569
|
+
/** @const */ var RESERVED_PROPERTIES = [
|
|
3570
|
+
SET_QUEUE_KEY,
|
|
3571
|
+
SET_ONCE_QUEUE_KEY,
|
|
3572
|
+
UNSET_QUEUE_KEY,
|
|
3573
|
+
ADD_QUEUE_KEY,
|
|
3574
|
+
APPEND_QUEUE_KEY,
|
|
3575
|
+
REMOVE_QUEUE_KEY,
|
|
3576
|
+
UNION_QUEUE_KEY,
|
|
3577
|
+
PEOPLE_DISTINCT_ID_KEY,
|
|
3578
|
+
ALIAS_ID_KEY,
|
|
3579
|
+
EVENT_TIMERS_KEY
|
|
3580
|
+
];
|
|
3561
3581
|
|
|
3562
|
-
|
|
3563
|
-
*
|
|
3582
|
+
/**
|
|
3583
|
+
* Mixpanel Persistence Object
|
|
3584
|
+
* @constructor
|
|
3564
3585
|
*/
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
/** @const */ var WINDOW_KEY = 'window';
|
|
3569
|
-
/** @const */ var UNIT_KEY = 'unit';
|
|
3570
|
-
/** @const */ var VALUE_KEY = 'value';
|
|
3571
|
-
/** @const */ var HOUR_KEY = 'hour';
|
|
3572
|
-
/** @const */ var DAY_KEY = 'day';
|
|
3573
|
-
/** @const */ var WEEK_KEY = 'week';
|
|
3574
|
-
/** @const */ var MONTH_KEY = 'month';
|
|
3575
|
-
|
|
3576
|
-
// Operands
|
|
3577
|
-
/** @const */ var EVENT_PROPERTY = 'event';
|
|
3578
|
-
/** @const */ var LITERAL_PROPERTY = 'literal';
|
|
3579
|
-
|
|
3580
|
-
// Binary Operators
|
|
3581
|
-
/** @const */ var AND_OPERATOR = 'and';
|
|
3582
|
-
/** @const */ var OR_OPERATOR = 'or';
|
|
3583
|
-
/** @const */ var IN_OPERATOR = 'in';
|
|
3584
|
-
/** @const */ var NOT_IN_OPERATOR = 'not in';
|
|
3585
|
-
/** @const */ var PLUS_OPERATOR = '+';
|
|
3586
|
-
/** @const */ var MINUS_OPERATOR = '-';
|
|
3587
|
-
/** @const */ var MUL_OPERATOR = '*';
|
|
3588
|
-
/** @const */ var DIV_OPERATOR = '/';
|
|
3589
|
-
/** @const */ var MOD_OPERATOR = '%';
|
|
3590
|
-
/** @const */ var EQUALS_OPERATOR = '==';
|
|
3591
|
-
/** @const */ var NOT_EQUALS_OPERATOR = '!=';
|
|
3592
|
-
/** @const */ var GREATER_OPERATOR = '>';
|
|
3593
|
-
/** @const */ var LESS_OPERATOR = '<';
|
|
3594
|
-
/** @const */ var GREATER_EQUAL_OPERATOR = '>=';
|
|
3595
|
-
/** @const */ var LESS_EQUAL_OPERATOR = '<=';
|
|
3596
|
-
|
|
3597
|
-
// Typecast Operators
|
|
3598
|
-
/** @const */ var BOOLEAN_OPERATOR = 'boolean';
|
|
3599
|
-
/** @const */ var DATETIME_OPERATOR = 'datetime';
|
|
3600
|
-
/** @const */ var LIST_OPERATOR = 'list';
|
|
3601
|
-
/** @const */ var NUMBER_OPERATOR = 'number';
|
|
3602
|
-
/** @const */ var STRING_OPERATOR = 'string';
|
|
3603
|
-
|
|
3604
|
-
// Unary Operators
|
|
3605
|
-
/** @const */ var NOT_OPERATOR = 'not';
|
|
3606
|
-
/** @const */ var DEFINED_OPERATOR = 'defined';
|
|
3607
|
-
/** @const */ var NOT_DEFINED_OPERATOR = 'not defined';
|
|
3608
|
-
|
|
3609
|
-
// Special literals
|
|
3610
|
-
/** @const */ var NOW_LITERAL = 'now';
|
|
3611
|
-
|
|
3612
|
-
// Type cast functions
|
|
3613
|
-
function toNumber(value) {
|
|
3614
|
-
if (value === null) {
|
|
3615
|
-
return null;
|
|
3616
|
-
}
|
|
3586
|
+
var MixpanelPersistence = function(config) {
|
|
3587
|
+
this['props'] = {};
|
|
3588
|
+
this.campaign_params_saved = false;
|
|
3617
3589
|
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
}
|
|
3623
|
-
return null;
|
|
3624
|
-
case 'boolean':
|
|
3625
|
-
return Number(value);
|
|
3626
|
-
case 'number':
|
|
3627
|
-
return value;
|
|
3628
|
-
case 'string':
|
|
3629
|
-
value = Number(value);
|
|
3630
|
-
if (!isNaN(value)) {
|
|
3631
|
-
return value;
|
|
3632
|
-
}
|
|
3633
|
-
return 0;
|
|
3590
|
+
if (config['persistence_name']) {
|
|
3591
|
+
this.name = 'mp_' + config['persistence_name'];
|
|
3592
|
+
} else {
|
|
3593
|
+
this.name = 'mp_' + config['token'] + '_mixpanel';
|
|
3634
3594
|
}
|
|
3635
|
-
return null;
|
|
3636
|
-
}
|
|
3637
3595
|
|
|
3638
|
-
|
|
3639
|
-
if (
|
|
3640
|
-
|
|
3596
|
+
var storage_type = config['persistence'];
|
|
3597
|
+
if (storage_type !== 'cookie' && storage_type !== 'localStorage') {
|
|
3598
|
+
console.critical('Unknown persistence type ' + storage_type + '; falling back to cookie');
|
|
3599
|
+
storage_type = config['persistence'] = 'cookie';
|
|
3641
3600
|
}
|
|
3642
3601
|
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
if (value === null) {
|
|
3648
|
-
return false;
|
|
3602
|
+
if (storage_type === 'localStorage' && _.localStorage.is_supported()) {
|
|
3603
|
+
this.storage = _.localStorage;
|
|
3604
|
+
} else {
|
|
3605
|
+
this.storage = _.cookie;
|
|
3649
3606
|
}
|
|
3650
3607
|
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
case 'string':
|
|
3657
|
-
return value.length > 0;
|
|
3658
|
-
case 'object':
|
|
3659
|
-
if (_.isArray(value) && value.length > 0) {
|
|
3660
|
-
return true;
|
|
3661
|
-
}
|
|
3662
|
-
if (_.isDate(value) && value.getTime() > 0) {
|
|
3663
|
-
return true;
|
|
3664
|
-
}
|
|
3665
|
-
if (_.isObject(value) && !_.isEmptyObject(value)) {
|
|
3666
|
-
return true;
|
|
3667
|
-
}
|
|
3668
|
-
return false;
|
|
3669
|
-
}
|
|
3670
|
-
return false;
|
|
3671
|
-
}
|
|
3608
|
+
this.load();
|
|
3609
|
+
this.update_config(config);
|
|
3610
|
+
this.upgrade(config);
|
|
3611
|
+
this.save();
|
|
3612
|
+
};
|
|
3672
3613
|
|
|
3673
|
-
function
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3614
|
+
MixpanelPersistence.prototype.properties = function() {
|
|
3615
|
+
var p = {};
|
|
3616
|
+
// Filter out reserved properties
|
|
3617
|
+
_.each(this['props'], function(v, k) {
|
|
3618
|
+
if (!_.include(RESERVED_PROPERTIES, k)) {
|
|
3619
|
+
p[k] = v;
|
|
3620
|
+
}
|
|
3621
|
+
});
|
|
3622
|
+
return p;
|
|
3623
|
+
};
|
|
3677
3624
|
|
|
3678
|
-
|
|
3679
|
-
|
|
3625
|
+
MixpanelPersistence.prototype.load = function() {
|
|
3626
|
+
if (this.disabled) { return; }
|
|
3680
3627
|
|
|
3681
|
-
|
|
3682
|
-
if (!op['operator'] || op['operator'] !== DATETIME_OPERATOR || !op['children'] || op['children'].length !== 1) {
|
|
3683
|
-
throw ('Invalid cast operator: datetime ' + op);
|
|
3684
|
-
}
|
|
3628
|
+
var entry = this.storage.parse(this.name);
|
|
3685
3629
|
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
return null;
|
|
3630
|
+
if (entry) {
|
|
3631
|
+
this['props'] = _.extend({}, entry);
|
|
3689
3632
|
}
|
|
3633
|
+
};
|
|
3690
3634
|
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
if (isNaN(d.getTime())) {
|
|
3696
|
-
return null;
|
|
3697
|
-
}
|
|
3698
|
-
return d;
|
|
3699
|
-
case 'object':
|
|
3700
|
-
if (_.isDate(v)) {
|
|
3701
|
-
return v;
|
|
3702
|
-
}
|
|
3703
|
-
}
|
|
3635
|
+
MixpanelPersistence.prototype.upgrade = function(config) {
|
|
3636
|
+
var upgrade_from_old_lib = config['upgrade'],
|
|
3637
|
+
old_cookie_name,
|
|
3638
|
+
old_cookie;
|
|
3704
3639
|
|
|
3705
|
-
|
|
3706
|
-
|
|
3640
|
+
if (upgrade_from_old_lib) {
|
|
3641
|
+
old_cookie_name = 'mp_super_properties';
|
|
3642
|
+
// Case where they had a custom cookie name before.
|
|
3643
|
+
if (typeof(upgrade_from_old_lib) === 'string') {
|
|
3644
|
+
old_cookie_name = upgrade_from_old_lib;
|
|
3645
|
+
}
|
|
3707
3646
|
|
|
3708
|
-
|
|
3709
|
-
if (!op['operator'] || op['operator'] !== LIST_OPERATOR || !op['children'] || op['children'].length !== 1) {
|
|
3710
|
-
throw ('Invalid cast operator: list ' + op);
|
|
3711
|
-
}
|
|
3647
|
+
old_cookie = this.storage.parse(old_cookie_name);
|
|
3712
3648
|
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
}
|
|
3649
|
+
// remove the cookie
|
|
3650
|
+
this.storage.remove(old_cookie_name);
|
|
3651
|
+
this.storage.remove(old_cookie_name, true);
|
|
3717
3652
|
|
|
3718
|
-
|
|
3719
|
-
|
|
3653
|
+
if (old_cookie) {
|
|
3654
|
+
this['props'] = _.extend(
|
|
3655
|
+
this['props'],
|
|
3656
|
+
old_cookie['all'],
|
|
3657
|
+
old_cookie['events']
|
|
3658
|
+
);
|
|
3659
|
+
}
|
|
3720
3660
|
}
|
|
3721
3661
|
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
throw ('Invalid cast operator: string ' + op);
|
|
3728
|
-
}
|
|
3662
|
+
if (!config['cookie_name'] && config['name'] !== 'mixpanel') {
|
|
3663
|
+
// special case to handle people with cookies of the form
|
|
3664
|
+
// mp_TOKEN_INSTANCENAME from the first release of this library
|
|
3665
|
+
old_cookie_name = 'mp_' + config['token'] + '_' + config['name'];
|
|
3666
|
+
old_cookie = this.storage.parse(old_cookie_name);
|
|
3729
3667
|
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
if (_.isDate(v)) {
|
|
3734
|
-
return v.toJSON();
|
|
3735
|
-
}
|
|
3736
|
-
return JSON.stringify(v);
|
|
3737
|
-
}
|
|
3738
|
-
return String(v);
|
|
3739
|
-
}
|
|
3668
|
+
if (old_cookie) {
|
|
3669
|
+
this.storage.remove(old_cookie_name);
|
|
3670
|
+
this.storage.remove(old_cookie_name, true);
|
|
3740
3671
|
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3672
|
+
// Save the prop values that were in the cookie from before -
|
|
3673
|
+
// this should only happen once as we delete the old one.
|
|
3674
|
+
this.register_once(old_cookie);
|
|
3675
|
+
}
|
|
3745
3676
|
}
|
|
3746
3677
|
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
function evaluateOr(op, properties) {
|
|
3751
|
-
if (!op['operator'] || op['operator'] !== OR_OPERATOR || !op['children'] || op['children'].length !== 2) {
|
|
3752
|
-
throw ('Invalid operator: OR ' + op);
|
|
3753
|
-
}
|
|
3678
|
+
if (this.storage === _.localStorage) {
|
|
3679
|
+
old_cookie = _.cookie.parse(this.name);
|
|
3754
3680
|
|
|
3755
|
-
|
|
3756
|
-
|
|
3681
|
+
_.cookie.remove(this.name);
|
|
3682
|
+
_.cookie.remove(this.name, true);
|
|
3757
3683
|
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
|
|
3684
|
+
if (old_cookie) {
|
|
3685
|
+
this.register_once(old_cookie);
|
|
3686
|
+
}
|
|
3761
3687
|
}
|
|
3762
|
-
|
|
3763
|
-
var rightValue = evaluateSelector(op['children'][1], properties);
|
|
3688
|
+
};
|
|
3764
3689
|
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3690
|
+
MixpanelPersistence.prototype.save = function() {
|
|
3691
|
+
if (this.disabled) { return; }
|
|
3692
|
+
this.storage.set(
|
|
3693
|
+
this.name,
|
|
3694
|
+
_.JSONEncode(this['props']),
|
|
3695
|
+
this.expire_days,
|
|
3696
|
+
this.cross_subdomain,
|
|
3697
|
+
this.secure,
|
|
3698
|
+
this.cross_site,
|
|
3699
|
+
this.cookie_domain
|
|
3700
|
+
);
|
|
3701
|
+
};
|
|
3768
3702
|
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
}
|
|
3703
|
+
MixpanelPersistence.prototype.remove = function() {
|
|
3704
|
+
// remove both domain and subdomain cookies
|
|
3705
|
+
this.storage.remove(this.name, false, this.cookie_domain);
|
|
3706
|
+
this.storage.remove(this.name, true, this.cookie_domain);
|
|
3707
|
+
};
|
|
3775
3708
|
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
var r = evaluateSelector(op['children'][1], properties);
|
|
3782
|
-
|
|
3783
|
-
if (typeof l === 'number' && typeof r === 'number') {
|
|
3784
|
-
return l + r;
|
|
3785
|
-
}
|
|
3786
|
-
if (typeof l === 'string' && typeof r === 'string') {
|
|
3787
|
-
return l + r;
|
|
3788
|
-
}
|
|
3789
|
-
return null;
|
|
3790
|
-
}
|
|
3791
|
-
|
|
3792
|
-
function evaluateArithmetic(op, properties) {
|
|
3793
|
-
if (!op['operator'] || [MINUS_OPERATOR, MUL_OPERATOR, DIV_OPERATOR, MOD_OPERATOR].indexOf(op['operator']) === -1 ||
|
|
3794
|
-
!op['children'] || op['children'].length < 2) {
|
|
3795
|
-
throw ('Invalid arithmetic operator ' + op);
|
|
3796
|
-
}
|
|
3797
|
-
|
|
3798
|
-
var l = evaluateSelector(op['children'][0], properties);
|
|
3799
|
-
var r = evaluateSelector(op['children'][1], properties);
|
|
3800
|
-
|
|
3801
|
-
if (typeof l === 'number' && typeof r === 'number') {
|
|
3802
|
-
switch (op['operator']) {
|
|
3803
|
-
case MINUS_OPERATOR:
|
|
3804
|
-
return l - r;
|
|
3805
|
-
case MUL_OPERATOR:
|
|
3806
|
-
return l * r;
|
|
3807
|
-
case DIV_OPERATOR:
|
|
3808
|
-
if (r !== 0) {
|
|
3809
|
-
return l / r;
|
|
3810
|
-
}
|
|
3811
|
-
return null;
|
|
3812
|
-
case MOD_OPERATOR:
|
|
3813
|
-
if (r === 0) {
|
|
3814
|
-
return null;
|
|
3815
|
-
}
|
|
3816
|
-
if (l === 0) {
|
|
3817
|
-
return 0;
|
|
3818
|
-
}
|
|
3819
|
-
if ((l < 0 && r > 0) || (l > 0 && r < 0)) {
|
|
3820
|
-
/* Mimic python modulo - result takes sign of the divisor
|
|
3821
|
-
* if one operand is negative. */
|
|
3822
|
-
return -(Math.floor(l / r) * r - l);
|
|
3823
|
-
}
|
|
3824
|
-
return l % r;
|
|
3825
|
-
default:
|
|
3826
|
-
throw('Unknown operator: ' + op['operator']);
|
|
3827
|
-
}
|
|
3828
|
-
}
|
|
3829
|
-
|
|
3830
|
-
return null;
|
|
3831
|
-
}
|
|
3832
|
-
|
|
3833
|
-
function _isArrayEqual(l, r) {
|
|
3834
|
-
if (l === r) return true;
|
|
3835
|
-
if (l === null || r === null) return false;
|
|
3836
|
-
if (l.length !== r.length) return false;
|
|
3837
|
-
|
|
3838
|
-
for (var i = 0; i < l.length; i++) {
|
|
3839
|
-
if (l[i] !== r[i]) {
|
|
3840
|
-
return false;
|
|
3841
|
-
}
|
|
3842
|
-
}
|
|
3843
|
-
|
|
3844
|
-
return true;
|
|
3845
|
-
}
|
|
3846
|
-
|
|
3847
|
-
function _isEqual(l, r) {
|
|
3848
|
-
if ( l === null && l === r ) {
|
|
3849
|
-
return true;
|
|
3850
|
-
}
|
|
3851
|
-
if (typeof l === typeof r) {
|
|
3852
|
-
switch (typeof l) {
|
|
3853
|
-
case 'number':
|
|
3854
|
-
case 'string':
|
|
3855
|
-
case 'boolean':
|
|
3856
|
-
return l === r;
|
|
3857
|
-
case 'object':
|
|
3858
|
-
if (_.isArray(l) && _.isArray(r)) {
|
|
3859
|
-
return _isArrayEqual(l, r);
|
|
3860
|
-
}
|
|
3861
|
-
if (_.isDate(l) && _.isDate(r)) {
|
|
3862
|
-
return l.getTime() === r.getTime();
|
|
3863
|
-
}
|
|
3864
|
-
if (_.isObject(l) && _.isObject(r)) {
|
|
3865
|
-
return JSON.stringify(l) === JSON.stringify(r);
|
|
3866
|
-
}
|
|
3867
|
-
}
|
|
3868
|
-
}
|
|
3869
|
-
return false;
|
|
3870
|
-
}
|
|
3871
|
-
|
|
3872
|
-
function evaluateEquality(op, properties) {
|
|
3873
|
-
if (!op['operator'] || [EQUALS_OPERATOR, NOT_EQUALS_OPERATOR].indexOf(op['operator']) === -1 || !op['children'] || op['children'].length !== 2) {
|
|
3874
|
-
throw ('Invalid equality operator ' + op);
|
|
3875
|
-
}
|
|
3876
|
-
|
|
3877
|
-
var v = _isEqual(evaluateSelector(op['children'][0], properties), evaluateSelector(op['children'][1], properties));
|
|
3878
|
-
|
|
3879
|
-
switch (op['operator']) {
|
|
3880
|
-
case EQUALS_OPERATOR:
|
|
3881
|
-
return v;
|
|
3882
|
-
case NOT_EQUALS_OPERATOR:
|
|
3883
|
-
return !v;
|
|
3884
|
-
}
|
|
3885
|
-
}
|
|
3886
|
-
|
|
3887
|
-
function evaluateComparison(op, properties) {
|
|
3888
|
-
if (!op['operator'] ||
|
|
3889
|
-
[GREATER_OPERATOR, GREATER_EQUAL_OPERATOR, LESS_OPERATOR, LESS_EQUAL_OPERATOR].indexOf(op['operator']) === -1 ||
|
|
3890
|
-
!op['children'] || op['children'].length !== 2) {
|
|
3891
|
-
throw ('Invalid comparison operator ' + op);
|
|
3892
|
-
}
|
|
3893
|
-
var l = evaluateSelector(op['children'][0], properties);
|
|
3894
|
-
var r = evaluateSelector(op['children'][1], properties);
|
|
3895
|
-
|
|
3896
|
-
if (typeof(l) === typeof(r)) {
|
|
3897
|
-
if (typeof(r) === 'number' || _.isDate(r)) {
|
|
3898
|
-
l = toNumber(l);
|
|
3899
|
-
r = toNumber(r);
|
|
3900
|
-
switch (op['operator']) {
|
|
3901
|
-
case GREATER_OPERATOR:
|
|
3902
|
-
return l > r;
|
|
3903
|
-
case GREATER_EQUAL_OPERATOR:
|
|
3904
|
-
return l >= r;
|
|
3905
|
-
case LESS_OPERATOR:
|
|
3906
|
-
return l < r;
|
|
3907
|
-
case LESS_EQUAL_OPERATOR:
|
|
3908
|
-
return l <= r;
|
|
3909
|
-
}
|
|
3910
|
-
} else if (typeof(r) === 'string') {
|
|
3911
|
-
var compare = l.localeCompare(r);
|
|
3912
|
-
switch (op['operator']) {
|
|
3913
|
-
case GREATER_OPERATOR:
|
|
3914
|
-
return compare > 0;
|
|
3915
|
-
case GREATER_EQUAL_OPERATOR:
|
|
3916
|
-
return compare >= 0;
|
|
3917
|
-
case LESS_OPERATOR:
|
|
3918
|
-
return compare < 0;
|
|
3919
|
-
case LESS_EQUAL_OPERATOR:
|
|
3920
|
-
return compare <= 0;
|
|
3921
|
-
}
|
|
3922
|
-
}
|
|
3923
|
-
}
|
|
3924
|
-
|
|
3925
|
-
return null;
|
|
3926
|
-
}
|
|
3927
|
-
|
|
3928
|
-
function evaluateDefined(op, properties) {
|
|
3929
|
-
if (!op['operator'] || [DEFINED_OPERATOR, NOT_DEFINED_OPERATOR].indexOf(op['operator']) === -1 ||
|
|
3930
|
-
!op['children'] || op['children'].length !== 1) {
|
|
3931
|
-
throw ('Invalid defined/not defined operator: ' + op);
|
|
3932
|
-
}
|
|
3933
|
-
|
|
3934
|
-
var b = evaluateSelector(op['children'][0], properties) !== null;
|
|
3935
|
-
if (op['operator'] === NOT_DEFINED_OPERATOR) {
|
|
3936
|
-
return !b;
|
|
3937
|
-
}
|
|
3938
|
-
|
|
3939
|
-
return b;
|
|
3940
|
-
}
|
|
3941
|
-
|
|
3942
|
-
function evaluateNot(op, properties) {
|
|
3943
|
-
if (!op['operator'] || op['operator'] !== NOT_OPERATOR || !op['children'] || op['children'].length !== 1) {
|
|
3944
|
-
throw ('Invalid not operator: ' + op);
|
|
3945
|
-
}
|
|
3946
|
-
|
|
3947
|
-
var v = evaluateSelector(op['children'][0], properties);
|
|
3948
|
-
if (v === null) {
|
|
3949
|
-
return true;
|
|
3950
|
-
}
|
|
3951
|
-
|
|
3952
|
-
if (typeof(v) === 'boolean') {
|
|
3953
|
-
return !v;
|
|
3954
|
-
}
|
|
3955
|
-
|
|
3956
|
-
return null;
|
|
3957
|
-
}
|
|
3958
|
-
|
|
3959
|
-
function evaluateOperator(op, properties) {
|
|
3960
|
-
if (!op['operator']) {
|
|
3961
|
-
throw ('Invalid operator: operator key missing ' + op);
|
|
3962
|
-
}
|
|
3963
|
-
|
|
3964
|
-
switch (op['operator']) {
|
|
3965
|
-
case AND_OPERATOR:
|
|
3966
|
-
return evaluateAnd(op, properties);
|
|
3967
|
-
case OR_OPERATOR:
|
|
3968
|
-
return evaluateOr(op, properties);
|
|
3969
|
-
case IN_OPERATOR:
|
|
3970
|
-
case NOT_IN_OPERATOR:
|
|
3971
|
-
return evaluateIn(op, properties);
|
|
3972
|
-
case PLUS_OPERATOR:
|
|
3973
|
-
return evaluatePlus(op, properties);
|
|
3974
|
-
case MINUS_OPERATOR:
|
|
3975
|
-
case MUL_OPERATOR:
|
|
3976
|
-
case DIV_OPERATOR:
|
|
3977
|
-
case MOD_OPERATOR:
|
|
3978
|
-
return evaluateArithmetic(op, properties);
|
|
3979
|
-
case EQUALS_OPERATOR:
|
|
3980
|
-
case NOT_EQUALS_OPERATOR:
|
|
3981
|
-
return evaluateEquality(op, properties);
|
|
3982
|
-
case GREATER_OPERATOR:
|
|
3983
|
-
case LESS_OPERATOR:
|
|
3984
|
-
case GREATER_EQUAL_OPERATOR:
|
|
3985
|
-
case LESS_EQUAL_OPERATOR:
|
|
3986
|
-
return evaluateComparison(op, properties);
|
|
3987
|
-
case BOOLEAN_OPERATOR:
|
|
3988
|
-
return evaluateBoolean(op, properties);
|
|
3989
|
-
case DATETIME_OPERATOR:
|
|
3990
|
-
return evaluateDateTime(op, properties);
|
|
3991
|
-
case LIST_OPERATOR:
|
|
3992
|
-
return evaluateList(op, properties);
|
|
3993
|
-
case NUMBER_OPERATOR:
|
|
3994
|
-
return evaluateNumber(op, properties);
|
|
3995
|
-
case STRING_OPERATOR:
|
|
3996
|
-
return evaluateString(op, properties);
|
|
3997
|
-
case DEFINED_OPERATOR:
|
|
3998
|
-
case NOT_DEFINED_OPERATOR:
|
|
3999
|
-
return evaluateDefined(op, properties);
|
|
4000
|
-
case NOT_OPERATOR:
|
|
4001
|
-
return evaluateNot(op, properties);
|
|
4002
|
-
}
|
|
4003
|
-
}
|
|
4004
|
-
|
|
4005
|
-
function evaluateWindow(value) {
|
|
4006
|
-
var win = value[WINDOW_KEY];
|
|
4007
|
-
if (!win || !win[UNIT_KEY] || !win[VALUE_KEY]) {
|
|
4008
|
-
throw('Invalid window: missing required keys ' + JSON.stringify(value));
|
|
4009
|
-
}
|
|
4010
|
-
var out = new Date();
|
|
4011
|
-
switch (win[UNIT_KEY]) {
|
|
4012
|
-
case HOUR_KEY:
|
|
4013
|
-
out.setTime(out.getTime() + (win[VALUE_KEY]*-1*60*60*1000));
|
|
4014
|
-
break;
|
|
4015
|
-
case DAY_KEY:
|
|
4016
|
-
out.setTime(out.getTime() + (win[VALUE_KEY]*-1*24*60*60*1000));
|
|
4017
|
-
break;
|
|
4018
|
-
case WEEK_KEY:
|
|
4019
|
-
out.setTime(out.getTime() + (win[VALUE_KEY]*-1*7*24*60*60*1000));
|
|
4020
|
-
break;
|
|
4021
|
-
case MONTH_KEY:
|
|
4022
|
-
out.setTime(out.getTime() + (win[VALUE_KEY]*-1*30*24*60*60*1000));
|
|
4023
|
-
break;
|
|
4024
|
-
default:
|
|
4025
|
-
throw('Invalid unit: ' + win[UNIT_KEY]);
|
|
4026
|
-
}
|
|
4027
|
-
|
|
4028
|
-
return out;
|
|
4029
|
-
}
|
|
4030
|
-
|
|
4031
|
-
function evaluateOperand(op, properties) {
|
|
4032
|
-
if (!op['property'] || !op['value']) {
|
|
4033
|
-
throw('Invalid operand: missing required keys ' + op);
|
|
4034
|
-
}
|
|
4035
|
-
switch (op['property']) {
|
|
4036
|
-
case EVENT_PROPERTY:
|
|
4037
|
-
if (properties[op['value']] !== undefined) {
|
|
4038
|
-
return properties[op['value']];
|
|
4039
|
-
}
|
|
4040
|
-
return null;
|
|
4041
|
-
case LITERAL_PROPERTY:
|
|
4042
|
-
if (op['value'] === NOW_LITERAL) {
|
|
4043
|
-
return new Date();
|
|
4044
|
-
}
|
|
4045
|
-
if (typeof(op['value']) === 'object') {
|
|
4046
|
-
return evaluateWindow(op['value']);
|
|
4047
|
-
}
|
|
4048
|
-
return op['value'];
|
|
4049
|
-
default:
|
|
4050
|
-
throw('Invalid operand: Invalid property type ' + op['property']);
|
|
4051
|
-
}
|
|
4052
|
-
}
|
|
4053
|
-
|
|
4054
|
-
function evaluateSelector(filters, properties) {
|
|
4055
|
-
if (filters[PROPERTY_KEY]) {
|
|
4056
|
-
return evaluateOperand(filters, properties);
|
|
4057
|
-
}
|
|
4058
|
-
if (filters[OPERATOR_KEY]) {
|
|
4059
|
-
return evaluateOperator(filters, properties);
|
|
4060
|
-
}
|
|
4061
|
-
}
|
|
4062
|
-
|
|
4063
|
-
// Internal class for notification display
|
|
4064
|
-
|
|
4065
|
-
var MixpanelNotification = function(notif_data, mixpanel_instance) {
|
|
4066
|
-
_.bind_instance_methods(this);
|
|
4067
|
-
|
|
4068
|
-
this.mixpanel = mixpanel_instance;
|
|
4069
|
-
this.persistence = this.mixpanel['persistence'];
|
|
4070
|
-
this.resource_protocol = this.mixpanel.get_config('inapp_protocol');
|
|
4071
|
-
this.cdn_host = this.mixpanel.get_config('cdn');
|
|
4072
|
-
|
|
4073
|
-
this.campaign_id = _.escapeHTML(notif_data['id']);
|
|
4074
|
-
this.message_id = _.escapeHTML(notif_data['message_id']);
|
|
4075
|
-
|
|
4076
|
-
this.body = (_.escapeHTML(notif_data['body']) || '').replace(/\n/g, '<br/>');
|
|
4077
|
-
this.cta = _.escapeHTML(notif_data['cta']) || 'Close';
|
|
4078
|
-
this.notif_type = _.escapeHTML(notif_data['type']) || 'takeover';
|
|
4079
|
-
this.style = _.escapeHTML(notif_data['style']) || 'light';
|
|
4080
|
-
this.title = _.escapeHTML(notif_data['title']) || '';
|
|
4081
|
-
this.video_width = MixpanelNotification.VIDEO_WIDTH;
|
|
4082
|
-
this.video_height = MixpanelNotification.VIDEO_HEIGHT;
|
|
4083
|
-
|
|
4084
|
-
this.display_triggers = notif_data['display_triggers'] || [];
|
|
4085
|
-
|
|
4086
|
-
// These fields are url-sanitized in the backend already.
|
|
4087
|
-
this.dest_url = notif_data['cta_url'] || null;
|
|
4088
|
-
this.image_url = notif_data['image_url'] || null;
|
|
4089
|
-
this.thumb_image_url = notif_data['thumb_image_url'] || null;
|
|
4090
|
-
this.video_url = notif_data['video_url'] || null;
|
|
4091
|
-
|
|
4092
|
-
if (this.thumb_image_url && this.thumb_image_url.indexOf('//') === 0) {
|
|
4093
|
-
this.thumb_image_url = this.thumb_image_url.replace('//', this.resource_protocol);
|
|
4094
|
-
}
|
|
4095
|
-
|
|
4096
|
-
this.clickthrough = true;
|
|
4097
|
-
if (!this.dest_url) {
|
|
4098
|
-
this.dest_url = '#dismiss';
|
|
4099
|
-
this.clickthrough = false;
|
|
4100
|
-
}
|
|
4101
|
-
|
|
4102
|
-
this.mini = this.notif_type === 'mini';
|
|
4103
|
-
if (!this.mini) {
|
|
4104
|
-
this.notif_type = 'takeover';
|
|
4105
|
-
}
|
|
4106
|
-
this.notif_width = !this.mini ? MixpanelNotification.NOTIF_WIDTH : MixpanelNotification.NOTIF_WIDTH_MINI;
|
|
4107
|
-
|
|
4108
|
-
this._set_client_config();
|
|
4109
|
-
this.imgs_to_preload = this._init_image_html();
|
|
4110
|
-
this._init_video();
|
|
4111
|
-
};
|
|
4112
|
-
|
|
4113
|
-
MixpanelNotification.ANIM_TIME = 200;
|
|
4114
|
-
MixpanelNotification.MARKUP_PREFIX = 'mixpanel-notification';
|
|
4115
|
-
MixpanelNotification.BG_OPACITY = 0.6;
|
|
4116
|
-
MixpanelNotification.NOTIF_TOP = 25;
|
|
4117
|
-
MixpanelNotification.NOTIF_START_TOP = 200;
|
|
4118
|
-
MixpanelNotification.NOTIF_WIDTH = 388;
|
|
4119
|
-
MixpanelNotification.NOTIF_WIDTH_MINI = 420;
|
|
4120
|
-
MixpanelNotification.NOTIF_HEIGHT_MINI = 85;
|
|
4121
|
-
MixpanelNotification.THUMB_BORDER_SIZE = 5;
|
|
4122
|
-
MixpanelNotification.THUMB_IMG_SIZE = 60;
|
|
4123
|
-
MixpanelNotification.THUMB_OFFSET = Math.round(MixpanelNotification.THUMB_IMG_SIZE / 2);
|
|
4124
|
-
MixpanelNotification.VIDEO_WIDTH = 595;
|
|
4125
|
-
MixpanelNotification.VIDEO_HEIGHT = 334;
|
|
4126
|
-
|
|
4127
|
-
MixpanelNotification.prototype.show = function() {
|
|
4128
|
-
var self = this;
|
|
4129
|
-
this._set_client_config();
|
|
4130
|
-
|
|
4131
|
-
// don't display until HTML body exists
|
|
4132
|
-
if (!this.body_el) {
|
|
4133
|
-
setTimeout(function() { self.show(); }, 300);
|
|
4134
|
-
return;
|
|
4135
|
-
}
|
|
4136
|
-
|
|
4137
|
-
this._init_styles();
|
|
4138
|
-
this._init_notification_el();
|
|
4139
|
-
|
|
4140
|
-
// wait for any images to load before showing notification
|
|
4141
|
-
this._preload_images(this._attach_and_animate);
|
|
4142
|
-
};
|
|
4143
|
-
|
|
4144
|
-
MixpanelNotification.prototype.dismiss = _.safewrap(function() {
|
|
4145
|
-
if (!this.marked_as_shown) {
|
|
4146
|
-
// unexpected condition: user interacted with notif even though we didn't consider it
|
|
4147
|
-
// visible (see _mark_as_shown()); send tracking signals to mark delivery
|
|
4148
|
-
this._mark_delivery({'invisible': true});
|
|
4149
|
-
}
|
|
4150
|
-
|
|
4151
|
-
var exiting_el = this.showing_video ? this._get_el('video') : this._get_notification_display_el();
|
|
4152
|
-
if (this.use_transitions) {
|
|
4153
|
-
this._remove_class('bg', 'visible');
|
|
4154
|
-
this._add_class(exiting_el, 'exiting');
|
|
4155
|
-
setTimeout(this._remove_notification_el, MixpanelNotification.ANIM_TIME);
|
|
4156
|
-
} else {
|
|
4157
|
-
var notif_attr, notif_start, notif_goal;
|
|
4158
|
-
if (this.mini) {
|
|
4159
|
-
notif_attr = 'right';
|
|
4160
|
-
notif_start = 20;
|
|
4161
|
-
notif_goal = -100;
|
|
4162
|
-
} else {
|
|
4163
|
-
notif_attr = 'top';
|
|
4164
|
-
notif_start = MixpanelNotification.NOTIF_TOP;
|
|
4165
|
-
notif_goal = MixpanelNotification.NOTIF_START_TOP + MixpanelNotification.NOTIF_TOP;
|
|
4166
|
-
}
|
|
4167
|
-
this._animate_els([
|
|
4168
|
-
{
|
|
4169
|
-
el: this._get_el('bg'),
|
|
4170
|
-
attr: 'opacity',
|
|
4171
|
-
start: MixpanelNotification.BG_OPACITY,
|
|
4172
|
-
goal: 0.0
|
|
4173
|
-
},
|
|
4174
|
-
{
|
|
4175
|
-
el: exiting_el,
|
|
4176
|
-
attr: 'opacity',
|
|
4177
|
-
start: 1.0,
|
|
4178
|
-
goal: 0.0
|
|
4179
|
-
},
|
|
4180
|
-
{
|
|
4181
|
-
el: exiting_el,
|
|
4182
|
-
attr: notif_attr,
|
|
4183
|
-
start: notif_start,
|
|
4184
|
-
goal: notif_goal
|
|
4185
|
-
}
|
|
4186
|
-
], MixpanelNotification.ANIM_TIME, this._remove_notification_el);
|
|
4187
|
-
}
|
|
4188
|
-
});
|
|
4189
|
-
|
|
4190
|
-
MixpanelNotification.prototype._add_class = _.safewrap(function(el, class_name) {
|
|
4191
|
-
class_name = MixpanelNotification.MARKUP_PREFIX + '-' + class_name;
|
|
4192
|
-
if (typeof el === 'string') {
|
|
4193
|
-
el = this._get_el(el);
|
|
4194
|
-
}
|
|
4195
|
-
if (!el.className) {
|
|
4196
|
-
el.className = class_name;
|
|
4197
|
-
} else if (!~(' ' + el.className + ' ').indexOf(' ' + class_name + ' ')) {
|
|
4198
|
-
el.className += ' ' + class_name;
|
|
4199
|
-
}
|
|
4200
|
-
});
|
|
4201
|
-
MixpanelNotification.prototype._remove_class = _.safewrap(function(el, class_name) {
|
|
4202
|
-
class_name = MixpanelNotification.MARKUP_PREFIX + '-' + class_name;
|
|
4203
|
-
if (typeof el === 'string') {
|
|
4204
|
-
el = this._get_el(el);
|
|
4205
|
-
}
|
|
4206
|
-
if (el.className) {
|
|
4207
|
-
el.className = (' ' + el.className + ' ')
|
|
4208
|
-
.replace(' ' + class_name + ' ', '')
|
|
4209
|
-
.replace(/^[\s\xA0]+/, '')
|
|
4210
|
-
.replace(/[\s\xA0]+$/, '');
|
|
4211
|
-
}
|
|
4212
|
-
});
|
|
4213
|
-
|
|
4214
|
-
MixpanelNotification.prototype._animate_els = _.safewrap(function(anims, mss, done_cb, start_time) {
|
|
4215
|
-
var self = this,
|
|
4216
|
-
in_progress = false,
|
|
4217
|
-
ai, anim,
|
|
4218
|
-
cur_time = 1 * new Date(), time_diff;
|
|
4219
|
-
|
|
4220
|
-
start_time = start_time || cur_time;
|
|
4221
|
-
time_diff = cur_time - start_time;
|
|
4222
|
-
|
|
4223
|
-
for (ai = 0; ai < anims.length; ai++) {
|
|
4224
|
-
anim = anims[ai];
|
|
4225
|
-
if (typeof anim.val === 'undefined') {
|
|
4226
|
-
anim.val = anim.start;
|
|
4227
|
-
}
|
|
4228
|
-
if (anim.val !== anim.goal) {
|
|
4229
|
-
in_progress = true;
|
|
4230
|
-
var anim_diff = anim.goal - anim.start,
|
|
4231
|
-
anim_dir = anim.goal >= anim.start ? 1 : -1;
|
|
4232
|
-
anim.val = anim.start + anim_diff * time_diff / mss;
|
|
4233
|
-
if (anim.attr !== 'opacity') {
|
|
4234
|
-
anim.val = Math.round(anim.val);
|
|
4235
|
-
}
|
|
4236
|
-
if ((anim_dir > 0 && anim.val >= anim.goal) || (anim_dir < 0 && anim.val <= anim.goal)) {
|
|
4237
|
-
anim.val = anim.goal;
|
|
4238
|
-
}
|
|
4239
|
-
}
|
|
4240
|
-
}
|
|
4241
|
-
if (!in_progress) {
|
|
4242
|
-
if (done_cb) {
|
|
4243
|
-
done_cb();
|
|
4244
|
-
}
|
|
4245
|
-
return;
|
|
4246
|
-
}
|
|
4247
|
-
|
|
4248
|
-
for (ai = 0; ai < anims.length; ai++) {
|
|
4249
|
-
anim = anims[ai];
|
|
4250
|
-
if (anim.el) {
|
|
4251
|
-
var suffix = anim.attr === 'opacity' ? '' : 'px';
|
|
4252
|
-
anim.el.style[anim.attr] = String(anim.val) + suffix;
|
|
4253
|
-
}
|
|
4254
|
-
}
|
|
4255
|
-
setTimeout(function() { self._animate_els(anims, mss, done_cb, start_time); }, 10);
|
|
4256
|
-
});
|
|
4257
|
-
|
|
4258
|
-
MixpanelNotification.prototype._attach_and_animate = _.safewrap(function() {
|
|
4259
|
-
var self = this;
|
|
4260
|
-
|
|
4261
|
-
// no possibility to double-display
|
|
4262
|
-
if (this.shown || this._get_shown_campaigns()[this.campaign_id]) {
|
|
4263
|
-
return;
|
|
4264
|
-
}
|
|
4265
|
-
this.shown = true;
|
|
4266
|
-
|
|
4267
|
-
this.body_el.appendChild(this.notification_el);
|
|
4268
|
-
setTimeout(function() {
|
|
4269
|
-
var notif_el = self._get_notification_display_el();
|
|
4270
|
-
if (self.use_transitions) {
|
|
4271
|
-
if (!self.mini) {
|
|
4272
|
-
self._add_class('bg', 'visible');
|
|
4273
|
-
}
|
|
4274
|
-
self._add_class(notif_el, 'visible');
|
|
4275
|
-
self._mark_as_shown();
|
|
4276
|
-
} else {
|
|
4277
|
-
var notif_attr, notif_start, notif_goal;
|
|
4278
|
-
if (self.mini) {
|
|
4279
|
-
notif_attr = 'right';
|
|
4280
|
-
notif_start = -100;
|
|
4281
|
-
notif_goal = 20;
|
|
4282
|
-
} else {
|
|
4283
|
-
notif_attr = 'top';
|
|
4284
|
-
notif_start = MixpanelNotification.NOTIF_START_TOP + MixpanelNotification.NOTIF_TOP;
|
|
4285
|
-
notif_goal = MixpanelNotification.NOTIF_TOP;
|
|
4286
|
-
}
|
|
4287
|
-
self._animate_els([
|
|
4288
|
-
{
|
|
4289
|
-
el: self._get_el('bg'),
|
|
4290
|
-
attr: 'opacity',
|
|
4291
|
-
start: 0.0,
|
|
4292
|
-
goal: MixpanelNotification.BG_OPACITY
|
|
4293
|
-
},
|
|
4294
|
-
{
|
|
4295
|
-
el: notif_el,
|
|
4296
|
-
attr: 'opacity',
|
|
4297
|
-
start: 0.0,
|
|
4298
|
-
goal: 1.0
|
|
4299
|
-
},
|
|
4300
|
-
{
|
|
4301
|
-
el: notif_el,
|
|
4302
|
-
attr: notif_attr,
|
|
4303
|
-
start: notif_start,
|
|
4304
|
-
goal: notif_goal
|
|
4305
|
-
}
|
|
4306
|
-
], MixpanelNotification.ANIM_TIME, self._mark_as_shown);
|
|
4307
|
-
}
|
|
4308
|
-
}, 100);
|
|
4309
|
-
_.register_event(self._get_el('cancel'), 'click', function(e) {
|
|
4310
|
-
e.preventDefault();
|
|
4311
|
-
self.dismiss();
|
|
4312
|
-
});
|
|
4313
|
-
var click_el = self._get_el('button') ||
|
|
4314
|
-
self._get_el('mini-content');
|
|
4315
|
-
_.register_event(click_el, 'click', function(e) {
|
|
4316
|
-
e.preventDefault();
|
|
4317
|
-
if (self.show_video) {
|
|
4318
|
-
self._track_event('$campaign_open', {'$resource_type': 'video'});
|
|
4319
|
-
self._switch_to_video();
|
|
4320
|
-
} else {
|
|
4321
|
-
self.dismiss();
|
|
4322
|
-
if (self.clickthrough) {
|
|
4323
|
-
var tracking_cb = null;
|
|
4324
|
-
if (self.mixpanel.get_config('inapp_link_new_window')) {
|
|
4325
|
-
window.open(self.dest_url);
|
|
4326
|
-
} else {
|
|
4327
|
-
tracking_cb = function() {
|
|
4328
|
-
window.location.href = self.dest_url;
|
|
4329
|
-
};
|
|
4330
|
-
}
|
|
4331
|
-
self._track_event('$campaign_open', {'$resource_type': 'link'}, tracking_cb);
|
|
4332
|
-
}
|
|
4333
|
-
}
|
|
4334
|
-
});
|
|
4335
|
-
});
|
|
4336
|
-
|
|
4337
|
-
MixpanelNotification.prototype._get_el = function(id) {
|
|
4338
|
-
return document.getElementById(MixpanelNotification.MARKUP_PREFIX + '-' + id);
|
|
4339
|
-
};
|
|
4340
|
-
|
|
4341
|
-
MixpanelNotification.prototype._get_notification_display_el = function() {
|
|
4342
|
-
return this._get_el(this.notif_type);
|
|
4343
|
-
};
|
|
4344
|
-
|
|
4345
|
-
MixpanelNotification.prototype._get_shown_campaigns = function() {
|
|
4346
|
-
return this.persistence['props'][CAMPAIGN_IDS_KEY] || (this.persistence['props'][CAMPAIGN_IDS_KEY] = {});
|
|
4347
|
-
};
|
|
4348
|
-
|
|
4349
|
-
MixpanelNotification.prototype._matches_event_data = _.safewrap(function(event_data) {
|
|
4350
|
-
var event_name = event_data['event'] || '';
|
|
4351
|
-
for (var i = 0; i < this.display_triggers.length; i++) {
|
|
4352
|
-
var display_trigger = this.display_triggers[i];
|
|
4353
|
-
var match_event = display_trigger['event'] || '';
|
|
4354
|
-
if (match_event === '$any_event' || event_name === display_trigger['event']) {
|
|
4355
|
-
if (display_trigger['selector'] && !_.isEmptyObject(display_trigger['selector'])) {
|
|
4356
|
-
if (evaluateSelector(display_trigger['selector'], event_data['properties'])) {
|
|
4357
|
-
return true;
|
|
4358
|
-
}
|
|
4359
|
-
} else {
|
|
4360
|
-
return true;
|
|
4361
|
-
}
|
|
4362
|
-
}
|
|
4363
|
-
}
|
|
4364
|
-
return false;
|
|
4365
|
-
});
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
MixpanelNotification.prototype._browser_lte = function(browser, version) {
|
|
4369
|
-
return this.browser_versions[browser] && this.browser_versions[browser] <= version;
|
|
4370
|
-
};
|
|
4371
|
-
|
|
4372
|
-
MixpanelNotification.prototype._init_image_html = function() {
|
|
4373
|
-
var imgs_to_preload = [];
|
|
4374
|
-
|
|
4375
|
-
if (!this.mini) {
|
|
4376
|
-
if (this.image_url) {
|
|
4377
|
-
imgs_to_preload.push(this.image_url);
|
|
4378
|
-
this.img_html = '<img id="img" src="' + this.image_url + '"/>';
|
|
4379
|
-
} else {
|
|
4380
|
-
this.img_html = '';
|
|
4381
|
-
}
|
|
4382
|
-
if (this.thumb_image_url) {
|
|
4383
|
-
imgs_to_preload.push(this.thumb_image_url);
|
|
4384
|
-
this.thumb_img_html =
|
|
4385
|
-
'<div id="thumbborder-wrapper"><div id="thumbborder"></div></div>' +
|
|
4386
|
-
'<img id="thumbnail"' +
|
|
4387
|
-
' src="' + this.thumb_image_url + '"' +
|
|
4388
|
-
' width="' + MixpanelNotification.THUMB_IMG_SIZE + '"' +
|
|
4389
|
-
' height="' + MixpanelNotification.THUMB_IMG_SIZE + '"' +
|
|
4390
|
-
'/>' +
|
|
4391
|
-
'<div id="thumbspacer"></div>';
|
|
4392
|
-
} else {
|
|
4393
|
-
this.thumb_img_html = '';
|
|
4394
|
-
}
|
|
4395
|
-
} else {
|
|
4396
|
-
this.thumb_image_url = this.thumb_image_url || (this.cdn_host + '/site_media/images/icons/notifications/mini-news-dark.png');
|
|
4397
|
-
imgs_to_preload.push(this.thumb_image_url);
|
|
4398
|
-
}
|
|
4399
|
-
|
|
4400
|
-
return imgs_to_preload;
|
|
4401
|
-
};
|
|
4402
|
-
|
|
4403
|
-
MixpanelNotification.prototype._init_notification_el = function() {
|
|
4404
|
-
var notification_html = '';
|
|
4405
|
-
var video_src = '';
|
|
4406
|
-
var video_html = '';
|
|
4407
|
-
var cancel_html = '<div id="cancel">' +
|
|
4408
|
-
'<div id="cancel-icon"></div>' +
|
|
4409
|
-
'</div>';
|
|
4410
|
-
|
|
4411
|
-
this.notification_el = document.createElement('div');
|
|
4412
|
-
this.notification_el.id = MixpanelNotification.MARKUP_PREFIX + '-wrapper';
|
|
4413
|
-
if (!this.mini) {
|
|
4414
|
-
// TAKEOVER notification
|
|
4415
|
-
var close_html = (this.clickthrough || this.show_video) ? '' : '<div id="button-close"></div>',
|
|
4416
|
-
play_html = this.show_video ? '<div id="button-play"></div>' : '';
|
|
4417
|
-
if (this._browser_lte('ie', 7)) {
|
|
4418
|
-
close_html = '';
|
|
4419
|
-
play_html = '';
|
|
4420
|
-
}
|
|
4421
|
-
notification_html =
|
|
4422
|
-
'<div id="takeover">' +
|
|
4423
|
-
this.thumb_img_html +
|
|
4424
|
-
'<div id="mainbox">' +
|
|
4425
|
-
cancel_html +
|
|
4426
|
-
'<div id="content">' +
|
|
4427
|
-
this.img_html +
|
|
4428
|
-
'<div id="title">' + this.title + '</div>' +
|
|
4429
|
-
'<div id="body">' + this.body + '</div>' +
|
|
4430
|
-
'<div id="tagline">' +
|
|
4431
|
-
'<a href="http://mixpanel.com?from=inapp" target="_blank">POWERED BY MIXPANEL</a>' +
|
|
4432
|
-
'</div>' +
|
|
4433
|
-
'</div>' +
|
|
4434
|
-
'<div id="button">' +
|
|
4435
|
-
close_html +
|
|
4436
|
-
'<a id="button-link" href="' + this.dest_url + '">' + this.cta + '</a>' +
|
|
4437
|
-
play_html +
|
|
4438
|
-
'</div>' +
|
|
4439
|
-
'</div>' +
|
|
4440
|
-
'</div>';
|
|
4441
|
-
} else {
|
|
4442
|
-
// MINI notification
|
|
4443
|
-
notification_html =
|
|
4444
|
-
'<div id="mini">' +
|
|
4445
|
-
'<div id="mainbox">' +
|
|
4446
|
-
cancel_html +
|
|
4447
|
-
'<div id="mini-content">' +
|
|
4448
|
-
'<div id="mini-icon">' +
|
|
4449
|
-
'<div id="mini-icon-img"></div>' +
|
|
4450
|
-
'</div>' +
|
|
4451
|
-
'<div id="body">' +
|
|
4452
|
-
'<div id="body-text"><div>' + this.body + '</div></div>' +
|
|
4453
|
-
'</div>' +
|
|
4454
|
-
'</div>' +
|
|
4455
|
-
'</div>' +
|
|
4456
|
-
'<div id="mini-border"></div>' +
|
|
4457
|
-
'</div>';
|
|
4458
|
-
}
|
|
4459
|
-
if (this.youtube_video) {
|
|
4460
|
-
video_src = this.resource_protocol + 'www.youtube.com/embed/' + this.youtube_video +
|
|
4461
|
-
'?wmode=transparent&showinfo=0&modestbranding=0&rel=0&autoplay=1&loop=0&vq=hd1080';
|
|
4462
|
-
if (this.yt_custom) {
|
|
4463
|
-
video_src += '&enablejsapi=1&html5=1&controls=0';
|
|
4464
|
-
video_html =
|
|
4465
|
-
'<div id="video-controls">' +
|
|
4466
|
-
'<div id="video-progress" class="video-progress-el">' +
|
|
4467
|
-
'<div id="video-progress-total" class="video-progress-el"></div>' +
|
|
4468
|
-
'<div id="video-elapsed" class="video-progress-el"></div>' +
|
|
4469
|
-
'</div>' +
|
|
4470
|
-
'<div id="video-time" class="video-progress-el"></div>' +
|
|
4471
|
-
'</div>';
|
|
4472
|
-
}
|
|
4473
|
-
} else if (this.vimeo_video) {
|
|
4474
|
-
video_src = this.resource_protocol + 'player.vimeo.com/video/' + this.vimeo_video + '?autoplay=1&title=0&byline=0&portrait=0';
|
|
4475
|
-
}
|
|
4476
|
-
if (this.show_video) {
|
|
4477
|
-
this.video_iframe =
|
|
4478
|
-
'<iframe id="' + MixpanelNotification.MARKUP_PREFIX + '-video-frame" ' +
|
|
4479
|
-
'width="' + this.video_width + '" height="' + this.video_height + '" ' +
|
|
4480
|
-
' src="' + video_src + '"' +
|
|
4481
|
-
' frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen="1" scrolling="no"' +
|
|
4482
|
-
'></iframe>';
|
|
4483
|
-
video_html =
|
|
4484
|
-
'<div id="video-' + (this.flip_animate ? '' : 'no') + 'flip">' +
|
|
4485
|
-
'<div id="video">' +
|
|
4486
|
-
'<div id="video-holder"></div>' +
|
|
4487
|
-
video_html +
|
|
4488
|
-
'</div>' +
|
|
4489
|
-
'</div>';
|
|
4490
|
-
}
|
|
4491
|
-
var main_html = video_html + notification_html;
|
|
4492
|
-
if (this.flip_animate) {
|
|
4493
|
-
main_html =
|
|
4494
|
-
(this.mini ? notification_html : '') +
|
|
4495
|
-
'<div id="flipcontainer"><div id="flipper">' +
|
|
4496
|
-
(this.mini ? video_html : main_html) +
|
|
4497
|
-
'</div></div>';
|
|
4498
|
-
}
|
|
4499
|
-
|
|
4500
|
-
this.notification_el.innerHTML =
|
|
4501
|
-
('<div id="overlay" class="' + this.notif_type + '">' +
|
|
4502
|
-
'<div id="campaignid-' + this.campaign_id + '">' +
|
|
4503
|
-
'<div id="bgwrapper">' +
|
|
4504
|
-
'<div id="bg"></div>' +
|
|
4505
|
-
main_html +
|
|
4506
|
-
'</div>' +
|
|
4507
|
-
'</div>' +
|
|
4508
|
-
'</div>')
|
|
4509
|
-
.replace(/class="/g, 'class="' + MixpanelNotification.MARKUP_PREFIX + '-')
|
|
4510
|
-
.replace(/id="/g, 'id="' + MixpanelNotification.MARKUP_PREFIX + '-');
|
|
4511
|
-
};
|
|
4512
|
-
|
|
4513
|
-
MixpanelNotification.prototype._init_styles = function() {
|
|
4514
|
-
if (this.style === 'dark') {
|
|
4515
|
-
this.style_vals = {
|
|
4516
|
-
bg: '#1d1f25',
|
|
4517
|
-
bg_actions: '#282b32',
|
|
4518
|
-
bg_hover: '#3a4147',
|
|
4519
|
-
bg_light: '#4a5157',
|
|
4520
|
-
border_gray: '#32353c',
|
|
4521
|
-
cancel_opacity: '0.4',
|
|
4522
|
-
mini_hover: '#2a3137',
|
|
4523
|
-
text_title: '#fff',
|
|
4524
|
-
text_main: '#9498a3',
|
|
4525
|
-
text_tagline: '#464851',
|
|
4526
|
-
text_hover: '#ddd'
|
|
4527
|
-
};
|
|
4528
|
-
} else {
|
|
4529
|
-
this.style_vals = {
|
|
4530
|
-
bg: '#fff',
|
|
4531
|
-
bg_actions: '#e7eaee',
|
|
4532
|
-
bg_hover: '#eceff3',
|
|
4533
|
-
bg_light: '#f5f5f5',
|
|
4534
|
-
border_gray: '#e4ecf2',
|
|
4535
|
-
cancel_opacity: '1.0',
|
|
4536
|
-
mini_hover: '#fafafa',
|
|
4537
|
-
text_title: '#5c6578',
|
|
4538
|
-
text_main: '#8b949b',
|
|
4539
|
-
text_tagline: '#ced9e6',
|
|
4540
|
-
text_hover: '#7c8598'
|
|
4541
|
-
};
|
|
4542
|
-
}
|
|
4543
|
-
var shadow = '0px 0px 35px 0px rgba(45, 49, 56, 0.7)',
|
|
4544
|
-
video_shadow = shadow,
|
|
4545
|
-
mini_shadow = shadow,
|
|
4546
|
-
thumb_total_size = MixpanelNotification.THUMB_IMG_SIZE + MixpanelNotification.THUMB_BORDER_SIZE * 2,
|
|
4547
|
-
anim_seconds = (MixpanelNotification.ANIM_TIME / 1000) + 's';
|
|
4548
|
-
if (this.mini) {
|
|
4549
|
-
shadow = 'none';
|
|
4550
|
-
}
|
|
4551
|
-
|
|
4552
|
-
// don't display on small viewports
|
|
4553
|
-
var notif_media_queries = {},
|
|
4554
|
-
min_width = MixpanelNotification.NOTIF_WIDTH_MINI + 20;
|
|
4555
|
-
notif_media_queries['@media only screen and (max-width: ' + (min_width - 1) + 'px)'] = {
|
|
4556
|
-
'#overlay': {
|
|
4557
|
-
'display': 'none'
|
|
4558
|
-
}
|
|
4559
|
-
};
|
|
4560
|
-
var notif_styles = {
|
|
4561
|
-
'.flipped': {
|
|
4562
|
-
'transform': 'rotateY(180deg)'
|
|
4563
|
-
},
|
|
4564
|
-
'#overlay': {
|
|
4565
|
-
'position': 'fixed',
|
|
4566
|
-
'top': '0',
|
|
4567
|
-
'left': '0',
|
|
4568
|
-
'width': '100%',
|
|
4569
|
-
'height': '100%',
|
|
4570
|
-
'overflow': 'auto',
|
|
4571
|
-
'text-align': 'center',
|
|
4572
|
-
'z-index': '10000',
|
|
4573
|
-
'font-family': '"Helvetica", "Arial", sans-serif',
|
|
4574
|
-
'-webkit-font-smoothing': 'antialiased',
|
|
4575
|
-
'-moz-osx-font-smoothing': 'grayscale'
|
|
4576
|
-
},
|
|
4577
|
-
'#overlay.mini': {
|
|
4578
|
-
'height': '0',
|
|
4579
|
-
'overflow': 'visible'
|
|
4580
|
-
},
|
|
4581
|
-
'#overlay a': {
|
|
4582
|
-
'width': 'initial',
|
|
4583
|
-
'padding': '0',
|
|
4584
|
-
'text-decoration': 'none',
|
|
4585
|
-
'text-transform': 'none',
|
|
4586
|
-
'color': 'inherit'
|
|
4587
|
-
},
|
|
4588
|
-
'#bgwrapper': {
|
|
4589
|
-
'position': 'relative',
|
|
4590
|
-
'width': '100%',
|
|
4591
|
-
'height': '100%'
|
|
4592
|
-
},
|
|
4593
|
-
'#bg': {
|
|
4594
|
-
'position': 'fixed',
|
|
4595
|
-
'top': '0',
|
|
4596
|
-
'left': '0',
|
|
4597
|
-
'width': '100%',
|
|
4598
|
-
'height': '100%',
|
|
4599
|
-
'min-width': this.doc_width * 4 + 'px',
|
|
4600
|
-
'min-height': this.doc_height * 4 + 'px',
|
|
4601
|
-
'background-color': 'black',
|
|
4602
|
-
'opacity': '0.0',
|
|
4603
|
-
'-ms-filter': 'progid:DXImageTransform.Microsoft.Alpha(Opacity=60)', // IE8
|
|
4604
|
-
'filter': 'alpha(opacity=60)', // IE5-7
|
|
4605
|
-
'transition': 'opacity ' + anim_seconds
|
|
4606
|
-
},
|
|
4607
|
-
'#bg.visible': {
|
|
4608
|
-
'opacity': MixpanelNotification.BG_OPACITY
|
|
4609
|
-
},
|
|
4610
|
-
'.mini #bg': {
|
|
4611
|
-
'width': '0',
|
|
4612
|
-
'height': '0',
|
|
4613
|
-
'min-width': '0'
|
|
4614
|
-
},
|
|
4615
|
-
'#flipcontainer': {
|
|
4616
|
-
'perspective': '1000px',
|
|
4617
|
-
'position': 'absolute',
|
|
4618
|
-
'width': '100%'
|
|
4619
|
-
},
|
|
4620
|
-
'#flipper': {
|
|
4621
|
-
'position': 'relative',
|
|
4622
|
-
'transform-style': 'preserve-3d',
|
|
4623
|
-
'transition': '0.3s'
|
|
4624
|
-
},
|
|
4625
|
-
'#takeover': {
|
|
4626
|
-
'position': 'absolute',
|
|
4627
|
-
'left': '50%',
|
|
4628
|
-
'width': MixpanelNotification.NOTIF_WIDTH + 'px',
|
|
4629
|
-
'margin-left': Math.round(-MixpanelNotification.NOTIF_WIDTH / 2) + 'px',
|
|
4630
|
-
'backface-visibility': 'hidden',
|
|
4631
|
-
'transform': 'rotateY(0deg)',
|
|
4632
|
-
'opacity': '0.0',
|
|
4633
|
-
'top': MixpanelNotification.NOTIF_START_TOP + 'px',
|
|
4634
|
-
'transition': 'opacity ' + anim_seconds + ', top ' + anim_seconds
|
|
4635
|
-
},
|
|
4636
|
-
'#takeover.visible': {
|
|
4637
|
-
'opacity': '1.0',
|
|
4638
|
-
'top': MixpanelNotification.NOTIF_TOP + 'px'
|
|
4639
|
-
},
|
|
4640
|
-
'#takeover.exiting': {
|
|
4641
|
-
'opacity': '0.0',
|
|
4642
|
-
'top': MixpanelNotification.NOTIF_START_TOP + 'px'
|
|
4643
|
-
},
|
|
4644
|
-
'#thumbspacer': {
|
|
4645
|
-
'height': MixpanelNotification.THUMB_OFFSET + 'px'
|
|
4646
|
-
},
|
|
4647
|
-
'#thumbborder-wrapper': {
|
|
4648
|
-
'position': 'absolute',
|
|
4649
|
-
'top': (-MixpanelNotification.THUMB_BORDER_SIZE) + 'px',
|
|
4650
|
-
'left': (MixpanelNotification.NOTIF_WIDTH / 2 - MixpanelNotification.THUMB_OFFSET - MixpanelNotification.THUMB_BORDER_SIZE) + 'px',
|
|
4651
|
-
'width': thumb_total_size + 'px',
|
|
4652
|
-
'height': (thumb_total_size / 2) + 'px',
|
|
4653
|
-
'overflow': 'hidden'
|
|
4654
|
-
},
|
|
4655
|
-
'#thumbborder': {
|
|
4656
|
-
'position': 'absolute',
|
|
4657
|
-
'width': thumb_total_size + 'px',
|
|
4658
|
-
'height': thumb_total_size + 'px',
|
|
4659
|
-
'border-radius': thumb_total_size + 'px',
|
|
4660
|
-
'background-color': this.style_vals.bg_actions,
|
|
4661
|
-
'opacity': '0.5'
|
|
4662
|
-
},
|
|
4663
|
-
'#thumbnail': {
|
|
4664
|
-
'position': 'absolute',
|
|
4665
|
-
'top': '0px',
|
|
4666
|
-
'left': (MixpanelNotification.NOTIF_WIDTH / 2 - MixpanelNotification.THUMB_OFFSET) + 'px',
|
|
4667
|
-
'width': MixpanelNotification.THUMB_IMG_SIZE + 'px',
|
|
4668
|
-
'height': MixpanelNotification.THUMB_IMG_SIZE + 'px',
|
|
4669
|
-
'overflow': 'hidden',
|
|
4670
|
-
'z-index': '100',
|
|
4671
|
-
'border-radius': MixpanelNotification.THUMB_IMG_SIZE + 'px'
|
|
4672
|
-
},
|
|
4673
|
-
'#mini': {
|
|
4674
|
-
'position': 'absolute',
|
|
4675
|
-
'right': '20px',
|
|
4676
|
-
'top': MixpanelNotification.NOTIF_TOP + 'px',
|
|
4677
|
-
'width': this.notif_width + 'px',
|
|
4678
|
-
'height': MixpanelNotification.NOTIF_HEIGHT_MINI * 2 + 'px',
|
|
4679
|
-
'margin-top': 20 - MixpanelNotification.NOTIF_HEIGHT_MINI + 'px',
|
|
4680
|
-
'backface-visibility': 'hidden',
|
|
4681
|
-
'opacity': '0.0',
|
|
4682
|
-
'transform': 'rotateX(90deg)',
|
|
4683
|
-
'transition': 'opacity 0.3s, transform 0.3s, right 0.3s'
|
|
4684
|
-
},
|
|
4685
|
-
'#mini.visible': {
|
|
4686
|
-
'opacity': '1.0',
|
|
4687
|
-
'transform': 'rotateX(0deg)'
|
|
4688
|
-
},
|
|
4689
|
-
'#mini.exiting': {
|
|
4690
|
-
'opacity': '0.0',
|
|
4691
|
-
'right': '-150px'
|
|
4692
|
-
},
|
|
4693
|
-
'#mainbox': {
|
|
4694
|
-
'border-radius': '4px',
|
|
4695
|
-
'box-shadow': shadow,
|
|
4696
|
-
'text-align': 'center',
|
|
4697
|
-
'background-color': this.style_vals.bg,
|
|
4698
|
-
'font-size': '14px',
|
|
4699
|
-
'color': this.style_vals.text_main
|
|
4700
|
-
},
|
|
4701
|
-
'#mini #mainbox': {
|
|
4702
|
-
'height': MixpanelNotification.NOTIF_HEIGHT_MINI + 'px',
|
|
4703
|
-
'margin-top': MixpanelNotification.NOTIF_HEIGHT_MINI + 'px',
|
|
4704
|
-
'border-radius': '3px',
|
|
4705
|
-
'transition': 'background-color ' + anim_seconds
|
|
4706
|
-
},
|
|
4707
|
-
'#mini-border': {
|
|
4708
|
-
'height': (MixpanelNotification.NOTIF_HEIGHT_MINI + 6) + 'px',
|
|
4709
|
-
'width': (MixpanelNotification.NOTIF_WIDTH_MINI + 6) + 'px',
|
|
4710
|
-
'position': 'absolute',
|
|
4711
|
-
'top': '-3px',
|
|
4712
|
-
'left': '-3px',
|
|
4713
|
-
'margin-top': MixpanelNotification.NOTIF_HEIGHT_MINI + 'px',
|
|
4714
|
-
'border-radius': '6px',
|
|
4715
|
-
'opacity': '0.25',
|
|
4716
|
-
'background-color': '#fff',
|
|
4717
|
-
'z-index': '-1',
|
|
4718
|
-
'box-shadow': mini_shadow
|
|
4719
|
-
},
|
|
4720
|
-
'#mini-icon': {
|
|
4721
|
-
'position': 'relative',
|
|
4722
|
-
'display': 'inline-block',
|
|
4723
|
-
'width': '75px',
|
|
4724
|
-
'height': MixpanelNotification.NOTIF_HEIGHT_MINI + 'px',
|
|
4725
|
-
'border-radius': '3px 0 0 3px',
|
|
4726
|
-
'background-color': this.style_vals.bg_actions,
|
|
4727
|
-
'background': 'linear-gradient(135deg, ' + this.style_vals.bg_light + ' 0%, ' + this.style_vals.bg_actions + ' 100%)',
|
|
4728
|
-
'transition': 'background-color ' + anim_seconds
|
|
4729
|
-
},
|
|
4730
|
-
'#mini:hover #mini-icon': {
|
|
4731
|
-
'background-color': this.style_vals.mini_hover
|
|
4732
|
-
},
|
|
4733
|
-
'#mini:hover #mainbox': {
|
|
4734
|
-
'background-color': this.style_vals.mini_hover
|
|
4735
|
-
},
|
|
4736
|
-
'#mini-icon-img': {
|
|
4737
|
-
'position': 'absolute',
|
|
4738
|
-
'background-image': 'url(' + this.thumb_image_url + ')',
|
|
4739
|
-
'width': '48px',
|
|
4740
|
-
'height': '48px',
|
|
4741
|
-
'top': '20px',
|
|
4742
|
-
'left': '12px'
|
|
4743
|
-
},
|
|
4744
|
-
'#content': {
|
|
4745
|
-
'padding': '30px 20px 0px 20px'
|
|
4746
|
-
},
|
|
4747
|
-
'#mini-content': {
|
|
4748
|
-
'text-align': 'left',
|
|
4749
|
-
'height': MixpanelNotification.NOTIF_HEIGHT_MINI + 'px',
|
|
4750
|
-
'cursor': 'pointer'
|
|
4751
|
-
},
|
|
4752
|
-
'#img': {
|
|
4753
|
-
'width': '328px',
|
|
4754
|
-
'margin-top': '30px',
|
|
4755
|
-
'border-radius': '5px'
|
|
4756
|
-
},
|
|
4757
|
-
'#title': {
|
|
4758
|
-
'max-height': '600px',
|
|
4759
|
-
'overflow': 'hidden',
|
|
4760
|
-
'word-wrap': 'break-word',
|
|
4761
|
-
'padding': '25px 0px 20px 0px',
|
|
4762
|
-
'font-size': '19px',
|
|
4763
|
-
'font-weight': 'bold',
|
|
4764
|
-
'color': this.style_vals.text_title
|
|
4765
|
-
},
|
|
4766
|
-
'#body': {
|
|
4767
|
-
'max-height': '600px',
|
|
4768
|
-
'margin-bottom': '25px',
|
|
4769
|
-
'overflow': 'hidden',
|
|
4770
|
-
'word-wrap': 'break-word',
|
|
4771
|
-
'line-height': '21px',
|
|
4772
|
-
'font-size': '15px',
|
|
4773
|
-
'font-weight': 'normal',
|
|
4774
|
-
'text-align': 'left'
|
|
4775
|
-
},
|
|
4776
|
-
'#mini #body': {
|
|
4777
|
-
'display': 'inline-block',
|
|
4778
|
-
'max-width': '250px',
|
|
4779
|
-
'margin': '0 0 0 30px',
|
|
4780
|
-
'height': MixpanelNotification.NOTIF_HEIGHT_MINI + 'px',
|
|
4781
|
-
'font-size': '16px',
|
|
4782
|
-
'letter-spacing': '0.8px',
|
|
4783
|
-
'color': this.style_vals.text_title
|
|
4784
|
-
},
|
|
4785
|
-
'#mini #body-text': {
|
|
4786
|
-
'display': 'table',
|
|
4787
|
-
'height': MixpanelNotification.NOTIF_HEIGHT_MINI + 'px'
|
|
4788
|
-
},
|
|
4789
|
-
'#mini #body-text div': {
|
|
4790
|
-
'display': 'table-cell',
|
|
4791
|
-
'vertical-align': 'middle'
|
|
4792
|
-
},
|
|
4793
|
-
'#tagline': {
|
|
4794
|
-
'margin-bottom': '15px',
|
|
4795
|
-
'font-size': '10px',
|
|
4796
|
-
'font-weight': '600',
|
|
4797
|
-
'letter-spacing': '0.8px',
|
|
4798
|
-
'color': '#ccd7e0',
|
|
4799
|
-
'text-align': 'left'
|
|
4800
|
-
},
|
|
4801
|
-
'#tagline a': {
|
|
4802
|
-
'color': this.style_vals.text_tagline,
|
|
4803
|
-
'transition': 'color ' + anim_seconds
|
|
4804
|
-
},
|
|
4805
|
-
'#tagline a:hover': {
|
|
4806
|
-
'color': this.style_vals.text_hover
|
|
4807
|
-
},
|
|
4808
|
-
'#cancel': {
|
|
4809
|
-
'position': 'absolute',
|
|
4810
|
-
'right': '0',
|
|
4811
|
-
'width': '8px',
|
|
4812
|
-
'height': '8px',
|
|
4813
|
-
'padding': '10px',
|
|
4814
|
-
'border-radius': '20px',
|
|
4815
|
-
'margin': '12px 12px 0 0',
|
|
4816
|
-
'box-sizing': 'content-box',
|
|
4817
|
-
'cursor': 'pointer',
|
|
4818
|
-
'transition': 'background-color ' + anim_seconds
|
|
4819
|
-
},
|
|
4820
|
-
'#mini #cancel': {
|
|
4821
|
-
'margin': '7px 7px 0 0'
|
|
4822
|
-
},
|
|
4823
|
-
'#cancel-icon': {
|
|
4824
|
-
'width': '8px',
|
|
4825
|
-
'height': '8px',
|
|
4826
|
-
'overflow': 'hidden',
|
|
4827
|
-
'background-image': 'url(' + this.cdn_host + '/site_media/images/icons/notifications/cancel-x.png)',
|
|
4828
|
-
'opacity': this.style_vals.cancel_opacity
|
|
4829
|
-
},
|
|
4830
|
-
'#cancel:hover': {
|
|
4831
|
-
'background-color': this.style_vals.bg_hover
|
|
4832
|
-
},
|
|
4833
|
-
'#button': {
|
|
4834
|
-
'display': 'block',
|
|
4835
|
-
'height': '60px',
|
|
4836
|
-
'line-height': '60px',
|
|
4837
|
-
'text-align': 'center',
|
|
4838
|
-
'background-color': this.style_vals.bg_actions,
|
|
4839
|
-
'border-radius': '0 0 4px 4px',
|
|
4840
|
-
'overflow': 'hidden',
|
|
4841
|
-
'cursor': 'pointer',
|
|
4842
|
-
'transition': 'background-color ' + anim_seconds
|
|
4843
|
-
},
|
|
4844
|
-
'#button-close': {
|
|
4845
|
-
'display': 'inline-block',
|
|
4846
|
-
'width': '9px',
|
|
4847
|
-
'height': '60px',
|
|
4848
|
-
'margin-right': '8px',
|
|
4849
|
-
'vertical-align': 'top',
|
|
4850
|
-
'background-image': 'url(' + this.cdn_host + '/site_media/images/icons/notifications/close-x-' + this.style + '.png)',
|
|
4851
|
-
'background-repeat': 'no-repeat',
|
|
4852
|
-
'background-position': '0px 25px'
|
|
4853
|
-
},
|
|
4854
|
-
'#button-play': {
|
|
4855
|
-
'display': 'inline-block',
|
|
4856
|
-
'width': '30px',
|
|
4857
|
-
'height': '60px',
|
|
4858
|
-
'margin-left': '15px',
|
|
4859
|
-
'background-image': 'url(' + this.cdn_host + '/site_media/images/icons/notifications/play-' + this.style + '-small.png)',
|
|
4860
|
-
'background-repeat': 'no-repeat',
|
|
4861
|
-
'background-position': '0px 15px'
|
|
4862
|
-
},
|
|
4863
|
-
'a#button-link': {
|
|
4864
|
-
'display': 'inline-block',
|
|
4865
|
-
'vertical-align': 'top',
|
|
4866
|
-
'text-align': 'center',
|
|
4867
|
-
'font-size': '17px',
|
|
4868
|
-
'font-weight': 'bold',
|
|
4869
|
-
'overflow': 'hidden',
|
|
4870
|
-
'word-wrap': 'break-word',
|
|
4871
|
-
'color': this.style_vals.text_title,
|
|
4872
|
-
'transition': 'color ' + anim_seconds
|
|
4873
|
-
},
|
|
4874
|
-
'#button:hover': {
|
|
4875
|
-
'background-color': this.style_vals.bg_hover,
|
|
4876
|
-
'color': this.style_vals.text_hover
|
|
4877
|
-
},
|
|
4878
|
-
'#button:hover a': {
|
|
4879
|
-
'color': this.style_vals.text_hover
|
|
4880
|
-
},
|
|
4881
|
-
|
|
4882
|
-
'#video-noflip': {
|
|
4883
|
-
'position': 'relative',
|
|
4884
|
-
'top': (-this.video_height * 2) + 'px'
|
|
4885
|
-
},
|
|
4886
|
-
'#video-flip': {
|
|
4887
|
-
'backface-visibility': 'hidden',
|
|
4888
|
-
'transform': 'rotateY(180deg)'
|
|
4889
|
-
},
|
|
4890
|
-
'#video': {
|
|
4891
|
-
'position': 'absolute',
|
|
4892
|
-
'width': (this.video_width - 1) + 'px',
|
|
4893
|
-
'height': this.video_height + 'px',
|
|
4894
|
-
'top': MixpanelNotification.NOTIF_TOP + 'px',
|
|
4895
|
-
'margin-top': '100px',
|
|
4896
|
-
'left': '50%',
|
|
4897
|
-
'margin-left': Math.round(-this.video_width / 2) + 'px',
|
|
4898
|
-
'overflow': 'hidden',
|
|
4899
|
-
'border-radius': '5px',
|
|
4900
|
-
'box-shadow': video_shadow,
|
|
4901
|
-
'transform': 'translateZ(1px)', // webkit rendering bug http://stackoverflow.com/questions/18167981/clickable-link-area-unexpectedly-smaller-after-css-transform
|
|
4902
|
-
'transition': 'opacity ' + anim_seconds + ', top ' + anim_seconds
|
|
4903
|
-
},
|
|
4904
|
-
'#video.exiting': {
|
|
4905
|
-
'opacity': '0.0',
|
|
4906
|
-
'top': this.video_height + 'px'
|
|
4907
|
-
},
|
|
4908
|
-
'#video-holder': {
|
|
4909
|
-
'position': 'absolute',
|
|
4910
|
-
'width': (this.video_width - 1) + 'px',
|
|
4911
|
-
'height': this.video_height + 'px',
|
|
4912
|
-
'overflow': 'hidden',
|
|
4913
|
-
'border-radius': '5px'
|
|
4914
|
-
},
|
|
4915
|
-
'#video-frame': {
|
|
4916
|
-
'margin-left': '-1px',
|
|
4917
|
-
'width': this.video_width + 'px'
|
|
4918
|
-
},
|
|
4919
|
-
'#video-controls': {
|
|
4920
|
-
'opacity': '0',
|
|
4921
|
-
'transition': 'opacity 0.5s'
|
|
4922
|
-
},
|
|
4923
|
-
'#video:hover #video-controls': {
|
|
4924
|
-
'opacity': '1.0'
|
|
4925
|
-
},
|
|
4926
|
-
'#video .video-progress-el': {
|
|
4927
|
-
'position': 'absolute',
|
|
4928
|
-
'bottom': '0',
|
|
4929
|
-
'height': '25px',
|
|
4930
|
-
'border-radius': '0 0 0 5px'
|
|
4931
|
-
},
|
|
4932
|
-
'#video-progress': {
|
|
4933
|
-
'width': '90%'
|
|
4934
|
-
},
|
|
4935
|
-
'#video-progress-total': {
|
|
4936
|
-
'width': '100%',
|
|
4937
|
-
'background-color': this.style_vals.bg,
|
|
4938
|
-
'opacity': '0.7'
|
|
4939
|
-
},
|
|
4940
|
-
'#video-elapsed': {
|
|
4941
|
-
'width': '0',
|
|
4942
|
-
'background-color': '#6cb6f5',
|
|
4943
|
-
'opacity': '0.9'
|
|
4944
|
-
},
|
|
4945
|
-
'#video #video-time': {
|
|
4946
|
-
'width': '10%',
|
|
4947
|
-
'right': '0',
|
|
4948
|
-
'font-size': '11px',
|
|
4949
|
-
'line-height': '25px',
|
|
4950
|
-
'color': this.style_vals.text_main,
|
|
4951
|
-
'background-color': '#666',
|
|
4952
|
-
'border-radius': '0 0 5px 0'
|
|
4953
|
-
}
|
|
4954
|
-
};
|
|
4955
|
-
|
|
4956
|
-
// IE hacks
|
|
4957
|
-
if (this._browser_lte('ie', 8)) {
|
|
4958
|
-
_.extend(notif_styles, {
|
|
4959
|
-
'* html #overlay': {
|
|
4960
|
-
'position': 'absolute'
|
|
4961
|
-
},
|
|
4962
|
-
'* html #bg': {
|
|
4963
|
-
'position': 'absolute'
|
|
4964
|
-
},
|
|
4965
|
-
'html, body': {
|
|
4966
|
-
'height': '100%'
|
|
4967
|
-
}
|
|
4968
|
-
});
|
|
4969
|
-
}
|
|
4970
|
-
if (this._browser_lte('ie', 7)) {
|
|
4971
|
-
_.extend(notif_styles, {
|
|
4972
|
-
'#mini #body': {
|
|
4973
|
-
'display': 'inline',
|
|
4974
|
-
'zoom': '1',
|
|
4975
|
-
'border': '1px solid ' + this.style_vals.bg_hover
|
|
4976
|
-
},
|
|
4977
|
-
'#mini #body-text': {
|
|
4978
|
-
'padding': '20px'
|
|
4979
|
-
},
|
|
4980
|
-
'#mini #mini-icon': {
|
|
4981
|
-
'display': 'none'
|
|
4982
|
-
}
|
|
4983
|
-
});
|
|
4984
|
-
}
|
|
4985
|
-
|
|
4986
|
-
// add vendor-prefixed style rules
|
|
4987
|
-
var VENDOR_STYLES = [
|
|
4988
|
-
'backface-visibility', 'border-radius', 'box-shadow', 'opacity',
|
|
4989
|
-
'perspective', 'transform', 'transform-style', 'transition'
|
|
4990
|
-
],
|
|
4991
|
-
VENDOR_PREFIXES = ['khtml', 'moz', 'ms', 'o', 'webkit'];
|
|
4992
|
-
for (var selector in notif_styles) {
|
|
4993
|
-
for (var si = 0; si < VENDOR_STYLES.length; si++) {
|
|
4994
|
-
var prop = VENDOR_STYLES[si];
|
|
4995
|
-
if (prop in notif_styles[selector]) {
|
|
4996
|
-
var val = notif_styles[selector][prop];
|
|
4997
|
-
for (var pi = 0; pi < VENDOR_PREFIXES.length; pi++) {
|
|
4998
|
-
notif_styles[selector]['-' + VENDOR_PREFIXES[pi] + '-' + prop] = val;
|
|
4999
|
-
}
|
|
5000
|
-
}
|
|
5001
|
-
}
|
|
5002
|
-
}
|
|
5003
|
-
|
|
5004
|
-
var inject_styles = function(styles, media_queries) {
|
|
5005
|
-
var create_style_text = function(style_defs) {
|
|
5006
|
-
var st = '';
|
|
5007
|
-
for (var selector in style_defs) {
|
|
5008
|
-
var mp_selector = selector
|
|
5009
|
-
.replace(/#/g, '#' + MixpanelNotification.MARKUP_PREFIX + '-')
|
|
5010
|
-
.replace(/\./g, '.' + MixpanelNotification.MARKUP_PREFIX + '-');
|
|
5011
|
-
st += '\n' + mp_selector + ' {';
|
|
5012
|
-
var props = style_defs[selector];
|
|
5013
|
-
for (var k in props) {
|
|
5014
|
-
st += k + ':' + props[k] + ';';
|
|
5015
|
-
}
|
|
5016
|
-
st += '}';
|
|
5017
|
-
}
|
|
5018
|
-
return st;
|
|
5019
|
-
};
|
|
5020
|
-
var create_media_query_text = function(mq_defs) {
|
|
5021
|
-
var mqt = '';
|
|
5022
|
-
for (var mq in mq_defs) {
|
|
5023
|
-
mqt += '\n' + mq + ' {' + create_style_text(mq_defs[mq]) + '\n}';
|
|
5024
|
-
}
|
|
5025
|
-
return mqt;
|
|
5026
|
-
};
|
|
5027
|
-
|
|
5028
|
-
var style_text = create_style_text(styles) + create_media_query_text(media_queries),
|
|
5029
|
-
head_el = document.head || document.getElementsByTagName('head')[0] || document.documentElement,
|
|
5030
|
-
style_el = document.createElement('style');
|
|
5031
|
-
head_el.appendChild(style_el);
|
|
5032
|
-
style_el.setAttribute('type', 'text/css');
|
|
5033
|
-
if (style_el.styleSheet) { // IE
|
|
5034
|
-
style_el.styleSheet.cssText = style_text;
|
|
5035
|
-
} else {
|
|
5036
|
-
style_el.textContent = style_text;
|
|
5037
|
-
}
|
|
5038
|
-
};
|
|
5039
|
-
inject_styles(notif_styles, notif_media_queries);
|
|
5040
|
-
};
|
|
5041
|
-
|
|
5042
|
-
MixpanelNotification.prototype._init_video = _.safewrap(function() {
|
|
5043
|
-
if (!this.video_url) {
|
|
5044
|
-
return;
|
|
5045
|
-
}
|
|
5046
|
-
var self = this;
|
|
5047
|
-
|
|
5048
|
-
// Youtube iframe API compatibility
|
|
5049
|
-
self.yt_custom = 'postMessage' in window;
|
|
5050
|
-
|
|
5051
|
-
self.dest_url = self.video_url;
|
|
5052
|
-
var youtube_match = self.video_url.match(
|
|
5053
|
-
// http://stackoverflow.com/questions/2936467/parse-youtube-video-id-using-preg-match
|
|
5054
|
-
/(?:youtube(?:-nocookie)?\.com\/(?:[^/]+\/.+\/|(?:v|e(?:mbed)?)\/|.*[?&]v=)|youtu\.be\/)([^"&?/ ]{11})/i
|
|
5055
|
-
),
|
|
5056
|
-
vimeo_match = self.video_url.match(
|
|
5057
|
-
/vimeo\.com\/.*?(\d+)/i
|
|
5058
|
-
);
|
|
5059
|
-
if (youtube_match) {
|
|
5060
|
-
self.show_video = true;
|
|
5061
|
-
self.youtube_video = youtube_match[1];
|
|
5062
|
-
|
|
5063
|
-
if (self.yt_custom) {
|
|
5064
|
-
window['onYouTubeIframeAPIReady'] = function() {
|
|
5065
|
-
if (self._get_el('video-frame')) {
|
|
5066
|
-
self._yt_video_ready();
|
|
5067
|
-
}
|
|
5068
|
-
};
|
|
5069
|
-
|
|
5070
|
-
// load Youtube iframe API; see https://developers.google.com/youtube/iframe_api_reference
|
|
5071
|
-
var tag = document.createElement('script');
|
|
5072
|
-
tag.src = self.resource_protocol + 'www.youtube.com/iframe_api';
|
|
5073
|
-
var firstScriptTag = document.getElementsByTagName('script')[0];
|
|
5074
|
-
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
|
|
5075
|
-
}
|
|
5076
|
-
} else if (vimeo_match) {
|
|
5077
|
-
self.show_video = true;
|
|
5078
|
-
self.vimeo_video = vimeo_match[1];
|
|
5079
|
-
}
|
|
5080
|
-
|
|
5081
|
-
// IE <= 7, FF <= 3: fall through to video link rather than embedded player
|
|
5082
|
-
if (self._browser_lte('ie', 7) || self._browser_lte('firefox', 3)) {
|
|
5083
|
-
self.show_video = false;
|
|
5084
|
-
self.clickthrough = true;
|
|
5085
|
-
}
|
|
5086
|
-
});
|
|
5087
|
-
|
|
5088
|
-
MixpanelNotification.prototype._mark_as_shown = _.safewrap(function() {
|
|
5089
|
-
// click on background to dismiss
|
|
5090
|
-
var self = this;
|
|
5091
|
-
_.register_event(self._get_el('bg'), 'click', function() {
|
|
5092
|
-
self.dismiss();
|
|
5093
|
-
});
|
|
5094
|
-
|
|
5095
|
-
var get_style = function(el, style_name) {
|
|
5096
|
-
var styles = {};
|
|
5097
|
-
if (document.defaultView && document.defaultView.getComputedStyle) {
|
|
5098
|
-
styles = document.defaultView.getComputedStyle(el, null); // FF3 requires both args
|
|
5099
|
-
} else if (el.currentStyle) { // IE
|
|
5100
|
-
styles = el.currentStyle;
|
|
5101
|
-
}
|
|
5102
|
-
return styles[style_name];
|
|
5103
|
-
};
|
|
5104
|
-
|
|
5105
|
-
if (this.campaign_id) {
|
|
5106
|
-
var notif_el = this._get_el('overlay');
|
|
5107
|
-
if (notif_el && get_style(notif_el, 'visibility') !== 'hidden' && get_style(notif_el, 'display') !== 'none') {
|
|
5108
|
-
this._mark_delivery();
|
|
5109
|
-
}
|
|
5110
|
-
}
|
|
5111
|
-
});
|
|
5112
|
-
|
|
5113
|
-
MixpanelNotification.prototype._mark_delivery = _.safewrap(function(extra_props) {
|
|
5114
|
-
if (!this.marked_as_shown) {
|
|
5115
|
-
this.marked_as_shown = true;
|
|
5116
|
-
|
|
5117
|
-
if (this.campaign_id) {
|
|
5118
|
-
// mark notification shown (local cache)
|
|
5119
|
-
this._get_shown_campaigns()[this.campaign_id] = 1 * new Date();
|
|
5120
|
-
this.persistence.save();
|
|
5121
|
-
}
|
|
5122
|
-
|
|
5123
|
-
// track delivery
|
|
5124
|
-
this._track_event('$campaign_delivery', extra_props);
|
|
5125
|
-
|
|
5126
|
-
// mark notification shown (mixpanel property)
|
|
5127
|
-
this.mixpanel['people']['append']({
|
|
5128
|
-
'$campaigns': this.campaign_id,
|
|
5129
|
-
'$notifications': {
|
|
5130
|
-
'campaign_id': this.campaign_id,
|
|
5131
|
-
'message_id': this.message_id,
|
|
5132
|
-
'type': 'web',
|
|
5133
|
-
'time': new Date()
|
|
5134
|
-
}
|
|
5135
|
-
});
|
|
5136
|
-
}
|
|
5137
|
-
});
|
|
5138
|
-
|
|
5139
|
-
MixpanelNotification.prototype._preload_images = function(all_loaded_cb) {
|
|
5140
|
-
var self = this;
|
|
5141
|
-
if (this.imgs_to_preload.length === 0) {
|
|
5142
|
-
all_loaded_cb();
|
|
5143
|
-
return;
|
|
5144
|
-
}
|
|
5145
|
-
|
|
5146
|
-
var preloaded_imgs = 0;
|
|
5147
|
-
var img_objs = [];
|
|
5148
|
-
var onload = function() {
|
|
5149
|
-
preloaded_imgs++;
|
|
5150
|
-
if (preloaded_imgs === self.imgs_to_preload.length && all_loaded_cb) {
|
|
5151
|
-
all_loaded_cb();
|
|
5152
|
-
all_loaded_cb = null;
|
|
5153
|
-
}
|
|
5154
|
-
};
|
|
5155
|
-
for (var i = 0; i < this.imgs_to_preload.length; i++) {
|
|
5156
|
-
var img = new Image();
|
|
5157
|
-
img.onload = onload;
|
|
5158
|
-
img.src = this.imgs_to_preload[i];
|
|
5159
|
-
if (img.complete) {
|
|
5160
|
-
onload();
|
|
5161
|
-
}
|
|
5162
|
-
img_objs.push(img);
|
|
5163
|
-
}
|
|
5164
|
-
|
|
5165
|
-
// IE6/7 doesn't fire onload reliably
|
|
5166
|
-
if (this._browser_lte('ie', 7)) {
|
|
5167
|
-
setTimeout(function() {
|
|
5168
|
-
var imgs_loaded = true;
|
|
5169
|
-
for (i = 0; i < img_objs.length; i++) {
|
|
5170
|
-
if (!img_objs[i].complete) {
|
|
5171
|
-
imgs_loaded = false;
|
|
5172
|
-
}
|
|
5173
|
-
}
|
|
5174
|
-
if (imgs_loaded && all_loaded_cb) {
|
|
5175
|
-
all_loaded_cb();
|
|
5176
|
-
all_loaded_cb = null;
|
|
5177
|
-
}
|
|
5178
|
-
}, 500);
|
|
5179
|
-
}
|
|
5180
|
-
};
|
|
5181
|
-
|
|
5182
|
-
MixpanelNotification.prototype._remove_notification_el = _.safewrap(function() {
|
|
5183
|
-
window.clearInterval(this._video_progress_checker);
|
|
5184
|
-
this.notification_el.style.visibility = 'hidden';
|
|
5185
|
-
this.body_el.removeChild(this.notification_el);
|
|
5186
|
-
});
|
|
5187
|
-
|
|
5188
|
-
MixpanelNotification.prototype._set_client_config = function() {
|
|
5189
|
-
var get_browser_version = function(browser_ex) {
|
|
5190
|
-
var match = navigator.userAgent.match(browser_ex);
|
|
5191
|
-
return match && match[1];
|
|
5192
|
-
};
|
|
5193
|
-
this.browser_versions = {};
|
|
5194
|
-
this.browser_versions['chrome'] = get_browser_version(/Chrome\/(\d+)/);
|
|
5195
|
-
this.browser_versions['firefox'] = get_browser_version(/Firefox\/(\d+)/);
|
|
5196
|
-
this.browser_versions['ie'] = get_browser_version(/MSIE (\d+).+/);
|
|
5197
|
-
if (!this.browser_versions['ie'] && !(window.ActiveXObject) && 'ActiveXObject' in window) {
|
|
5198
|
-
this.browser_versions['ie'] = 11;
|
|
5199
|
-
}
|
|
5200
|
-
|
|
5201
|
-
this.body_el = document.body || document.getElementsByTagName('body')[0];
|
|
5202
|
-
if (this.body_el) {
|
|
5203
|
-
this.doc_width = Math.max(
|
|
5204
|
-
this.body_el.scrollWidth, document.documentElement.scrollWidth,
|
|
5205
|
-
this.body_el.offsetWidth, document.documentElement.offsetWidth,
|
|
5206
|
-
this.body_el.clientWidth, document.documentElement.clientWidth
|
|
5207
|
-
);
|
|
5208
|
-
this.doc_height = Math.max(
|
|
5209
|
-
this.body_el.scrollHeight, document.documentElement.scrollHeight,
|
|
5210
|
-
this.body_el.offsetHeight, document.documentElement.offsetHeight,
|
|
5211
|
-
this.body_el.clientHeight, document.documentElement.clientHeight
|
|
5212
|
-
);
|
|
5213
|
-
}
|
|
5214
|
-
|
|
5215
|
-
// detect CSS compatibility
|
|
5216
|
-
var ie_ver = this.browser_versions['ie'];
|
|
5217
|
-
var sample_styles = document.createElement('div').style,
|
|
5218
|
-
is_css_compatible = function(rule) {
|
|
5219
|
-
if (rule in sample_styles) {
|
|
5220
|
-
return true;
|
|
5221
|
-
}
|
|
5222
|
-
if (!ie_ver) {
|
|
5223
|
-
rule = rule[0].toUpperCase() + rule.slice(1);
|
|
5224
|
-
var props = ['O' + rule, 'Webkit' + rule, 'Moz' + rule];
|
|
5225
|
-
for (var i = 0; i < props.length; i++) {
|
|
5226
|
-
if (props[i] in sample_styles) {
|
|
5227
|
-
return true;
|
|
5228
|
-
}
|
|
5229
|
-
}
|
|
5230
|
-
}
|
|
5231
|
-
return false;
|
|
5232
|
-
};
|
|
5233
|
-
this.use_transitions = this.body_el &&
|
|
5234
|
-
is_css_compatible('transition') &&
|
|
5235
|
-
is_css_compatible('transform');
|
|
5236
|
-
this.flip_animate = (this.browser_versions['chrome'] >= 33 || this.browser_versions['firefox'] >= 15) &&
|
|
5237
|
-
this.body_el &&
|
|
5238
|
-
is_css_compatible('backfaceVisibility') &&
|
|
5239
|
-
is_css_compatible('perspective') &&
|
|
5240
|
-
is_css_compatible('transform');
|
|
5241
|
-
};
|
|
5242
|
-
|
|
5243
|
-
MixpanelNotification.prototype._switch_to_video = _.safewrap(function() {
|
|
5244
|
-
var self = this,
|
|
5245
|
-
anims = [
|
|
5246
|
-
{
|
|
5247
|
-
el: self._get_notification_display_el(),
|
|
5248
|
-
attr: 'opacity',
|
|
5249
|
-
start: 1.0,
|
|
5250
|
-
goal: 0.0
|
|
5251
|
-
},
|
|
5252
|
-
{
|
|
5253
|
-
el: self._get_notification_display_el(),
|
|
5254
|
-
attr: 'top',
|
|
5255
|
-
start: MixpanelNotification.NOTIF_TOP,
|
|
5256
|
-
goal: -500
|
|
5257
|
-
},
|
|
5258
|
-
{
|
|
5259
|
-
el: self._get_el('video-noflip'),
|
|
5260
|
-
attr: 'opacity',
|
|
5261
|
-
start: 0.0,
|
|
5262
|
-
goal: 1.0
|
|
5263
|
-
},
|
|
5264
|
-
{
|
|
5265
|
-
el: self._get_el('video-noflip'),
|
|
5266
|
-
attr: 'top',
|
|
5267
|
-
start: -self.video_height * 2,
|
|
5268
|
-
goal: 0
|
|
5269
|
-
}
|
|
5270
|
-
];
|
|
5271
|
-
|
|
5272
|
-
if (self.mini) {
|
|
5273
|
-
var bg = self._get_el('bg'),
|
|
5274
|
-
overlay = self._get_el('overlay');
|
|
5275
|
-
bg.style.width = '100%';
|
|
5276
|
-
bg.style.height = '100%';
|
|
5277
|
-
overlay.style.width = '100%';
|
|
5278
|
-
|
|
5279
|
-
self._add_class(self._get_notification_display_el(), 'exiting');
|
|
5280
|
-
self._add_class(bg, 'visible');
|
|
5281
|
-
|
|
5282
|
-
anims.push({
|
|
5283
|
-
el: self._get_el('bg'),
|
|
5284
|
-
attr: 'opacity',
|
|
5285
|
-
start: 0.0,
|
|
5286
|
-
goal: MixpanelNotification.BG_OPACITY
|
|
5287
|
-
});
|
|
5288
|
-
}
|
|
5289
|
-
|
|
5290
|
-
var video_el = self._get_el('video-holder');
|
|
5291
|
-
video_el.innerHTML = self.video_iframe;
|
|
5292
|
-
|
|
5293
|
-
var video_ready = function() {
|
|
5294
|
-
if (window['YT'] && window['YT']['loaded']) {
|
|
5295
|
-
self._yt_video_ready();
|
|
5296
|
-
}
|
|
5297
|
-
self.showing_video = true;
|
|
5298
|
-
self._get_notification_display_el().style.visibility = 'hidden';
|
|
5299
|
-
};
|
|
5300
|
-
if (self.flip_animate) {
|
|
5301
|
-
self._add_class('flipper', 'flipped');
|
|
5302
|
-
setTimeout(video_ready, MixpanelNotification.ANIM_TIME);
|
|
5303
|
-
} else {
|
|
5304
|
-
self._animate_els(anims, MixpanelNotification.ANIM_TIME, video_ready);
|
|
5305
|
-
}
|
|
5306
|
-
});
|
|
5307
|
-
|
|
5308
|
-
MixpanelNotification.prototype._track_event = function(event_name, properties, cb) {
|
|
5309
|
-
if (this.campaign_id) {
|
|
5310
|
-
properties = properties || {};
|
|
5311
|
-
properties = _.extend(properties, {
|
|
5312
|
-
'campaign_id': this.campaign_id,
|
|
5313
|
-
'message_id': this.message_id,
|
|
5314
|
-
'message_type': 'web_inapp',
|
|
5315
|
-
'message_subtype': this.notif_type
|
|
5316
|
-
});
|
|
5317
|
-
this.mixpanel['track'](event_name, properties, cb);
|
|
5318
|
-
} else if (cb) {
|
|
5319
|
-
cb.call();
|
|
5320
|
-
}
|
|
3709
|
+
// removes the storage entry and deletes all loaded data
|
|
3710
|
+
// forced name for tests
|
|
3711
|
+
MixpanelPersistence.prototype.clear = function() {
|
|
3712
|
+
this.remove();
|
|
3713
|
+
this['props'] = {};
|
|
5321
3714
|
};
|
|
5322
3715
|
|
|
5323
|
-
MixpanelNotification.prototype._yt_video_ready = _.safewrap(function() {
|
|
5324
|
-
var self = this;
|
|
5325
|
-
if (self.video_inited) {
|
|
5326
|
-
return;
|
|
5327
|
-
}
|
|
5328
|
-
self.video_inited = true;
|
|
5329
|
-
|
|
5330
|
-
var progress_bar = self._get_el('video-elapsed'),
|
|
5331
|
-
progress_time = self._get_el('video-time'),
|
|
5332
|
-
progress_el = self._get_el('video-progress');
|
|
5333
|
-
|
|
5334
|
-
new window['YT']['Player'](MixpanelNotification.MARKUP_PREFIX + '-video-frame', {
|
|
5335
|
-
'events': {
|
|
5336
|
-
'onReady': function(event) {
|
|
5337
|
-
var ytplayer = event['target'],
|
|
5338
|
-
video_duration = ytplayer['getDuration'](),
|
|
5339
|
-
pad = function(i) {
|
|
5340
|
-
return ('00' + i).slice(-2);
|
|
5341
|
-
},
|
|
5342
|
-
update_video_time = function(current_time) {
|
|
5343
|
-
var secs = Math.round(video_duration - current_time),
|
|
5344
|
-
mins = Math.floor(secs / 60),
|
|
5345
|
-
hours = Math.floor(mins / 60);
|
|
5346
|
-
secs -= mins * 60;
|
|
5347
|
-
mins -= hours * 60;
|
|
5348
|
-
progress_time.innerHTML = '-' + (hours ? hours + ':' : '') + pad(mins) + ':' + pad(secs);
|
|
5349
|
-
};
|
|
5350
|
-
update_video_time(0);
|
|
5351
|
-
self._video_progress_checker = window.setInterval(function() {
|
|
5352
|
-
var current_time = ytplayer['getCurrentTime']();
|
|
5353
|
-
progress_bar.style.width = (current_time / video_duration * 100) + '%';
|
|
5354
|
-
update_video_time(current_time);
|
|
5355
|
-
}, 250);
|
|
5356
|
-
_.register_event(progress_el, 'click', function(e) {
|
|
5357
|
-
var clickx = Math.max(0, e.pageX - progress_el.getBoundingClientRect().left);
|
|
5358
|
-
ytplayer['seekTo'](video_duration * clickx / progress_el.clientWidth, true);
|
|
5359
|
-
});
|
|
5360
|
-
}
|
|
5361
|
-
}
|
|
5362
|
-
});
|
|
5363
|
-
});
|
|
5364
|
-
|
|
5365
3716
|
/**
|
|
5366
|
-
|
|
5367
|
-
|
|
5368
|
-
|
|
5369
|
-
var MixpanelPeople = function() {};
|
|
5370
|
-
|
|
5371
|
-
_.extend(MixpanelPeople.prototype, apiActions);
|
|
5372
|
-
|
|
5373
|
-
MixpanelPeople.prototype._init = function(mixpanel_instance) {
|
|
5374
|
-
this._mixpanel = mixpanel_instance;
|
|
5375
|
-
};
|
|
5376
|
-
|
|
5377
|
-
/*
|
|
5378
|
-
* Set properties on a user record.
|
|
5379
|
-
*
|
|
5380
|
-
* ### Usage:
|
|
5381
|
-
*
|
|
5382
|
-
* mixpanel.people.set('gender', 'm');
|
|
5383
|
-
*
|
|
5384
|
-
* // or set multiple properties at once
|
|
5385
|
-
* mixpanel.people.set({
|
|
5386
|
-
* 'Company': 'Acme',
|
|
5387
|
-
* 'Plan': 'Premium',
|
|
5388
|
-
* 'Upgrade date': new Date()
|
|
5389
|
-
* });
|
|
5390
|
-
* // properties can be strings, integers, dates, or lists
|
|
5391
|
-
*
|
|
5392
|
-
* @param {Object|String} prop If a string, this is the name of the property. If an object, this is an associative array of names and values.
|
|
5393
|
-
* @param {*} [to] A value to set on the given property name
|
|
5394
|
-
* @param {Function} [callback] If provided, the callback will be called after tracking the event.
|
|
5395
|
-
*/
|
|
5396
|
-
MixpanelPeople.prototype.set = addOptOutCheckMixpanelPeople(function(prop, to, callback) {
|
|
5397
|
-
var data = this.set_action(prop, to);
|
|
5398
|
-
if (_.isObject(prop)) {
|
|
5399
|
-
callback = to;
|
|
5400
|
-
}
|
|
5401
|
-
// make sure that the referrer info has been updated and saved
|
|
5402
|
-
if (this._get_config('save_referrer')) {
|
|
5403
|
-
this._mixpanel['persistence'].update_referrer_info(document.referrer);
|
|
5404
|
-
}
|
|
5405
|
-
|
|
5406
|
-
// update $set object with default people properties
|
|
5407
|
-
data[SET_ACTION] = _.extend(
|
|
5408
|
-
{},
|
|
5409
|
-
_.info.people_properties(),
|
|
5410
|
-
this._mixpanel['persistence'].get_referrer_info(),
|
|
5411
|
-
data[SET_ACTION]
|
|
5412
|
-
);
|
|
5413
|
-
return this._send_request(data, callback);
|
|
5414
|
-
});
|
|
5415
|
-
|
|
5416
|
-
/*
|
|
5417
|
-
* Set properties on a user record, only if they do not yet exist.
|
|
5418
|
-
* This will not overwrite previous people property values, unlike
|
|
5419
|
-
* people.set().
|
|
5420
|
-
*
|
|
5421
|
-
* ### Usage:
|
|
5422
|
-
*
|
|
5423
|
-
* mixpanel.people.set_once('First Login Date', new Date());
|
|
5424
|
-
*
|
|
5425
|
-
* // or set multiple properties at once
|
|
5426
|
-
* mixpanel.people.set_once({
|
|
5427
|
-
* 'First Login Date': new Date(),
|
|
5428
|
-
* 'Starting Plan': 'Premium'
|
|
5429
|
-
* });
|
|
5430
|
-
*
|
|
5431
|
-
* // properties can be strings, integers or dates
|
|
5432
|
-
*
|
|
5433
|
-
* @param {Object|String} prop If a string, this is the name of the property. If an object, this is an associative array of names and values.
|
|
5434
|
-
* @param {*} [to] A value to set on the given property name
|
|
5435
|
-
* @param {Function} [callback] If provided, the callback will be called after tracking the event.
|
|
5436
|
-
*/
|
|
5437
|
-
MixpanelPeople.prototype.set_once = addOptOutCheckMixpanelPeople(function(prop, to, callback) {
|
|
5438
|
-
var data = this.set_once_action(prop, to);
|
|
5439
|
-
if (_.isObject(prop)) {
|
|
5440
|
-
callback = to;
|
|
5441
|
-
}
|
|
5442
|
-
return this._send_request(data, callback);
|
|
5443
|
-
});
|
|
5444
|
-
|
|
5445
|
-
/*
|
|
5446
|
-
* Unset properties on a user record (permanently removes the properties and their values from a profile).
|
|
5447
|
-
*
|
|
5448
|
-
* ### Usage:
|
|
5449
|
-
*
|
|
5450
|
-
* mixpanel.people.unset('gender');
|
|
5451
|
-
*
|
|
5452
|
-
* // or unset multiple properties at once
|
|
5453
|
-
* mixpanel.people.unset(['gender', 'Company']);
|
|
5454
|
-
*
|
|
5455
|
-
* @param {Array|String} prop If a string, this is the name of the property. If an array, this is a list of property names.
|
|
5456
|
-
* @param {Function} [callback] If provided, the callback will be called after tracking the event.
|
|
5457
|
-
*/
|
|
5458
|
-
MixpanelPeople.prototype.unset = addOptOutCheckMixpanelPeople(function(prop, callback) {
|
|
5459
|
-
var data = this.unset_action(prop);
|
|
5460
|
-
return this._send_request(data, callback);
|
|
5461
|
-
});
|
|
5462
|
-
|
|
5463
|
-
/*
|
|
5464
|
-
* Increment/decrement numeric people analytics properties.
|
|
5465
|
-
*
|
|
5466
|
-
* ### Usage:
|
|
5467
|
-
*
|
|
5468
|
-
* mixpanel.people.increment('page_views', 1);
|
|
5469
|
-
*
|
|
5470
|
-
* // or, for convenience, if you're just incrementing a counter by
|
|
5471
|
-
* // 1, you can simply do
|
|
5472
|
-
* mixpanel.people.increment('page_views');
|
|
5473
|
-
*
|
|
5474
|
-
* // to decrement a counter, pass a negative number
|
|
5475
|
-
* mixpanel.people.increment('credits_left', -1);
|
|
5476
|
-
*
|
|
5477
|
-
* // like mixpanel.people.set(), you can increment multiple
|
|
5478
|
-
* // properties at once:
|
|
5479
|
-
* mixpanel.people.increment({
|
|
5480
|
-
* counter1: 1,
|
|
5481
|
-
* counter2: 6
|
|
5482
|
-
* });
|
|
5483
|
-
*
|
|
5484
|
-
* @param {Object|String} prop If a string, this is the name of the property. If an object, this is an associative array of names and numeric values.
|
|
5485
|
-
* @param {Number} [by] An amount to increment the given property
|
|
5486
|
-
* @param {Function} [callback] If provided, the callback will be called after tracking the event.
|
|
5487
|
-
*/
|
|
5488
|
-
MixpanelPeople.prototype.increment = addOptOutCheckMixpanelPeople(function(prop, by, callback) {
|
|
5489
|
-
var data = {};
|
|
5490
|
-
var $add = {};
|
|
5491
|
-
if (_.isObject(prop)) {
|
|
5492
|
-
_.each(prop, function(v, k) {
|
|
5493
|
-
if (!this._is_reserved_property(k)) {
|
|
5494
|
-
if (isNaN(parseFloat(v))) {
|
|
5495
|
-
console.error('Invalid increment value passed to mixpanel.people.increment - must be a number');
|
|
5496
|
-
return;
|
|
5497
|
-
} else {
|
|
5498
|
-
$add[k] = v;
|
|
5499
|
-
}
|
|
5500
|
-
}
|
|
5501
|
-
}, this);
|
|
5502
|
-
callback = by;
|
|
5503
|
-
} else {
|
|
5504
|
-
// convenience: mixpanel.people.increment('property'); will
|
|
5505
|
-
// increment 'property' by 1
|
|
5506
|
-
if (_.isUndefined(by)) {
|
|
5507
|
-
by = 1;
|
|
5508
|
-
}
|
|
5509
|
-
$add[prop] = by;
|
|
5510
|
-
}
|
|
5511
|
-
data[ADD_ACTION] = $add;
|
|
5512
|
-
|
|
5513
|
-
return this._send_request(data, callback);
|
|
5514
|
-
});
|
|
5515
|
-
|
|
5516
|
-
/*
|
|
5517
|
-
* Append a value to a list-valued people analytics property.
|
|
5518
|
-
*
|
|
5519
|
-
* ### Usage:
|
|
5520
|
-
*
|
|
5521
|
-
* // append a value to a list, creating it if needed
|
|
5522
|
-
* mixpanel.people.append('pages_visited', 'homepage');
|
|
5523
|
-
*
|
|
5524
|
-
* // like mixpanel.people.set(), you can append multiple
|
|
5525
|
-
* // properties at once:
|
|
5526
|
-
* mixpanel.people.append({
|
|
5527
|
-
* list1: 'bob',
|
|
5528
|
-
* list2: 123
|
|
5529
|
-
* });
|
|
5530
|
-
*
|
|
5531
|
-
* @param {Object|String} list_name If a string, this is the name of the property. If an object, this is an associative array of names and values.
|
|
5532
|
-
* @param {*} [value] value An item to append to the list
|
|
5533
|
-
* @param {Function} [callback] If provided, the callback will be called after tracking the event.
|
|
5534
|
-
*/
|
|
5535
|
-
MixpanelPeople.prototype.append = addOptOutCheckMixpanelPeople(function(list_name, value, callback) {
|
|
5536
|
-
if (_.isObject(list_name)) {
|
|
5537
|
-
callback = value;
|
|
5538
|
-
}
|
|
5539
|
-
var data = this.append_action(list_name, value);
|
|
5540
|
-
return this._send_request(data, callback);
|
|
5541
|
-
});
|
|
5542
|
-
|
|
5543
|
-
/*
|
|
5544
|
-
* Remove a value from a list-valued people analytics property.
|
|
5545
|
-
*
|
|
5546
|
-
* ### Usage:
|
|
5547
|
-
*
|
|
5548
|
-
* mixpanel.people.remove('School', 'UCB');
|
|
5549
|
-
*
|
|
5550
|
-
* @param {Object|String} list_name If a string, this is the name of the property. If an object, this is an associative array of names and values.
|
|
5551
|
-
* @param {*} [value] value Item to remove from the list
|
|
5552
|
-
* @param {Function} [callback] If provided, the callback will be called after tracking the event.
|
|
5553
|
-
*/
|
|
5554
|
-
MixpanelPeople.prototype.remove = addOptOutCheckMixpanelPeople(function(list_name, value, callback) {
|
|
5555
|
-
if (_.isObject(list_name)) {
|
|
5556
|
-
callback = value;
|
|
5557
|
-
}
|
|
5558
|
-
var data = this.remove_action(list_name, value);
|
|
5559
|
-
return this._send_request(data, callback);
|
|
5560
|
-
});
|
|
5561
|
-
|
|
5562
|
-
/*
|
|
5563
|
-
* Merge a given list with a list-valued people analytics property,
|
|
5564
|
-
* excluding duplicate values.
|
|
5565
|
-
*
|
|
5566
|
-
* ### Usage:
|
|
5567
|
-
*
|
|
5568
|
-
* // merge a value to a list, creating it if needed
|
|
5569
|
-
* mixpanel.people.union('pages_visited', 'homepage');
|
|
5570
|
-
*
|
|
5571
|
-
* // like mixpanel.people.set(), you can append multiple
|
|
5572
|
-
* // properties at once:
|
|
5573
|
-
* mixpanel.people.union({
|
|
5574
|
-
* list1: 'bob',
|
|
5575
|
-
* list2: 123
|
|
5576
|
-
* });
|
|
5577
|
-
*
|
|
5578
|
-
* // like mixpanel.people.append(), you can append multiple
|
|
5579
|
-
* // values to the same list:
|
|
5580
|
-
* mixpanel.people.union({
|
|
5581
|
-
* list1: ['bob', 'billy']
|
|
5582
|
-
* });
|
|
5583
|
-
*
|
|
5584
|
-
* @param {Object|String} list_name If a string, this is the name of the property. If an object, this is an associative array of names and values.
|
|
5585
|
-
* @param {*} [value] Value / values to merge with the given property
|
|
5586
|
-
* @param {Function} [callback] If provided, the callback will be called after tracking the event.
|
|
3717
|
+
* @param {Object} props
|
|
3718
|
+
* @param {*=} default_value
|
|
3719
|
+
* @param {number=} days
|
|
5587
3720
|
*/
|
|
5588
|
-
|
|
5589
|
-
if (_.isObject(
|
|
5590
|
-
|
|
3721
|
+
MixpanelPersistence.prototype.register_once = function(props, default_value, days) {
|
|
3722
|
+
if (_.isObject(props)) {
|
|
3723
|
+
if (typeof(default_value) === 'undefined') { default_value = 'None'; }
|
|
3724
|
+
this.expire_days = (typeof(days) === 'undefined') ? this.default_expiry : days;
|
|
3725
|
+
|
|
3726
|
+
_.each(props, function(val, prop) {
|
|
3727
|
+
if (!this['props'].hasOwnProperty(prop) || this['props'][prop] === default_value) {
|
|
3728
|
+
this['props'][prop] = val;
|
|
3729
|
+
}
|
|
3730
|
+
}, this);
|
|
3731
|
+
|
|
3732
|
+
this.save();
|
|
3733
|
+
|
|
3734
|
+
return true;
|
|
5591
3735
|
}
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
});
|
|
3736
|
+
return false;
|
|
3737
|
+
};
|
|
5595
3738
|
|
|
5596
|
-
|
|
5597
|
-
*
|
|
5598
|
-
*
|
|
5599
|
-
* Mixpanel revenue report.
|
|
5600
|
-
*
|
|
5601
|
-
* ### Usage:
|
|
5602
|
-
*
|
|
5603
|
-
* // charge a user $50
|
|
5604
|
-
* mixpanel.people.track_charge(50);
|
|
5605
|
-
*
|
|
5606
|
-
* // charge a user $30.50 on the 2nd of january
|
|
5607
|
-
* mixpanel.people.track_charge(30.50, {
|
|
5608
|
-
* '$time': new Date('jan 1 2012')
|
|
5609
|
-
* });
|
|
5610
|
-
*
|
|
5611
|
-
* @param {Number} amount The amount of money charged to the current user
|
|
5612
|
-
* @param {Object} [properties] An associative array of properties associated with the charge
|
|
5613
|
-
* @param {Function} [callback] If provided, the callback will be called when the server responds
|
|
3739
|
+
/**
|
|
3740
|
+
* @param {Object} props
|
|
3741
|
+
* @param {number=} days
|
|
5614
3742
|
*/
|
|
5615
|
-
|
|
5616
|
-
if (
|
|
5617
|
-
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
|
|
3743
|
+
MixpanelPersistence.prototype.register = function(props, days) {
|
|
3744
|
+
if (_.isObject(props)) {
|
|
3745
|
+
this.expire_days = (typeof(days) === 'undefined') ? this.default_expiry : days;
|
|
3746
|
+
|
|
3747
|
+
_.extend(this['props'], props);
|
|
3748
|
+
|
|
3749
|
+
this.save();
|
|
3750
|
+
|
|
3751
|
+
return true;
|
|
3752
|
+
}
|
|
3753
|
+
return false;
|
|
3754
|
+
};
|
|
3755
|
+
|
|
3756
|
+
MixpanelPersistence.prototype.unregister = function(prop) {
|
|
3757
|
+
if (prop in this['props']) {
|
|
3758
|
+
delete this['props'][prop];
|
|
3759
|
+
this.save();
|
|
5622
3760
|
}
|
|
3761
|
+
};
|
|
5623
3762
|
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
|
|
3763
|
+
MixpanelPersistence.prototype.update_campaign_params = function() {
|
|
3764
|
+
if (!this.campaign_params_saved) {
|
|
3765
|
+
this.register_once(_.info.campaignParams());
|
|
3766
|
+
this.campaign_params_saved = true;
|
|
3767
|
+
}
|
|
3768
|
+
};
|
|
5628
3769
|
|
|
5629
|
-
|
|
5630
|
-
|
|
5631
|
-
* current user's people analytics profile.
|
|
5632
|
-
*
|
|
5633
|
-
* ### Usage:
|
|
5634
|
-
*
|
|
5635
|
-
* mixpanel.people.clear_charges();
|
|
5636
|
-
*
|
|
5637
|
-
* @param {Function} [callback] If provided, the callback will be called after tracking the event.
|
|
5638
|
-
*/
|
|
5639
|
-
MixpanelPeople.prototype.clear_charges = function(callback) {
|
|
5640
|
-
return this.set('$transactions', [], callback);
|
|
3770
|
+
MixpanelPersistence.prototype.update_search_keyword = function(referrer) {
|
|
3771
|
+
this.register(_.info.searchInfo(referrer));
|
|
5641
3772
|
};
|
|
5642
3773
|
|
|
5643
|
-
|
|
5644
|
-
|
|
5645
|
-
|
|
5646
|
-
|
|
5647
|
-
|
|
5648
|
-
|
|
5649
|
-
|
|
5650
|
-
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
|
|
5655
|
-
|
|
5656
|
-
|
|
3774
|
+
// EXPORTED METHOD, we test this directly.
|
|
3775
|
+
MixpanelPersistence.prototype.update_referrer_info = function(referrer) {
|
|
3776
|
+
// If referrer doesn't exist, we want to note the fact that it was type-in traffic.
|
|
3777
|
+
this.register_once({
|
|
3778
|
+
'$initial_referrer': referrer || '$direct',
|
|
3779
|
+
'$initial_referring_domain': _.info.referringDomain(referrer) || '$direct'
|
|
3780
|
+
}, '');
|
|
3781
|
+
};
|
|
3782
|
+
|
|
3783
|
+
MixpanelPersistence.prototype.get_referrer_info = function() {
|
|
3784
|
+
return _.strip_empty_properties({
|
|
3785
|
+
'$initial_referrer': this['props']['$initial_referrer'],
|
|
3786
|
+
'$initial_referring_domain': this['props']['$initial_referring_domain']
|
|
3787
|
+
});
|
|
3788
|
+
};
|
|
3789
|
+
|
|
3790
|
+
// safely fills the passed in object with stored properties,
|
|
3791
|
+
// does not override any properties defined in both
|
|
3792
|
+
// returns the passed in object
|
|
3793
|
+
MixpanelPersistence.prototype.safe_merge = function(props) {
|
|
3794
|
+
_.each(this['props'], function(val, prop) {
|
|
3795
|
+
if (!(prop in props)) {
|
|
3796
|
+
props[prop] = val;
|
|
3797
|
+
}
|
|
3798
|
+
});
|
|
3799
|
+
|
|
3800
|
+
return props;
|
|
3801
|
+
};
|
|
3802
|
+
|
|
3803
|
+
MixpanelPersistence.prototype.update_config = function(config) {
|
|
3804
|
+
this.default_expiry = this.expire_days = config['cookie_expiration'];
|
|
3805
|
+
this.set_disabled(config['disable_persistence']);
|
|
3806
|
+
this.set_cookie_domain(config['cookie_domain']);
|
|
3807
|
+
this.set_cross_site(config['cross_site_cookie']);
|
|
3808
|
+
this.set_cross_subdomain(config['cross_subdomain_cookie']);
|
|
3809
|
+
this.set_secure(config['secure_cookie']);
|
|
3810
|
+
};
|
|
3811
|
+
|
|
3812
|
+
MixpanelPersistence.prototype.set_disabled = function(disabled) {
|
|
3813
|
+
this.disabled = disabled;
|
|
3814
|
+
if (this.disabled) {
|
|
3815
|
+
this.remove();
|
|
3816
|
+
} else {
|
|
3817
|
+
this.save();
|
|
5657
3818
|
}
|
|
5658
|
-
var data = {'$delete': this._mixpanel.get_distinct_id()};
|
|
5659
|
-
return this._send_request(data);
|
|
5660
3819
|
};
|
|
5661
3820
|
|
|
5662
|
-
|
|
5663
|
-
|
|
3821
|
+
MixpanelPersistence.prototype.set_cookie_domain = function(cookie_domain) {
|
|
3822
|
+
if (cookie_domain !== this.cookie_domain) {
|
|
3823
|
+
this.remove();
|
|
3824
|
+
this.cookie_domain = cookie_domain;
|
|
3825
|
+
this.save();
|
|
3826
|
+
}
|
|
5664
3827
|
};
|
|
5665
3828
|
|
|
5666
|
-
|
|
5667
|
-
|
|
5668
|
-
|
|
5669
|
-
|
|
5670
|
-
|
|
5671
|
-
var had_persisted_distinct_id = this._mixpanel.get_property('$had_persisted_distinct_id');
|
|
5672
|
-
if (device_id) {
|
|
5673
|
-
data['$device_id'] = device_id;
|
|
3829
|
+
MixpanelPersistence.prototype.set_cross_site = function(cross_site) {
|
|
3830
|
+
if (cross_site !== this.cross_site) {
|
|
3831
|
+
this.cross_site = cross_site;
|
|
3832
|
+
this.remove();
|
|
3833
|
+
this.save();
|
|
5674
3834
|
}
|
|
5675
|
-
|
|
5676
|
-
|
|
3835
|
+
};
|
|
3836
|
+
|
|
3837
|
+
MixpanelPersistence.prototype.set_cross_subdomain = function(cross_subdomain) {
|
|
3838
|
+
if (cross_subdomain !== this.cross_subdomain) {
|
|
3839
|
+
this.cross_subdomain = cross_subdomain;
|
|
3840
|
+
this.remove();
|
|
3841
|
+
this.save();
|
|
5677
3842
|
}
|
|
5678
|
-
|
|
5679
|
-
|
|
3843
|
+
};
|
|
3844
|
+
|
|
3845
|
+
MixpanelPersistence.prototype.get_cross_subdomain = function() {
|
|
3846
|
+
return this.cross_subdomain;
|
|
3847
|
+
};
|
|
3848
|
+
|
|
3849
|
+
MixpanelPersistence.prototype.set_secure = function(secure) {
|
|
3850
|
+
if (secure !== this.secure) {
|
|
3851
|
+
this.secure = secure ? true : false;
|
|
3852
|
+
this.remove();
|
|
3853
|
+
this.save();
|
|
5680
3854
|
}
|
|
3855
|
+
};
|
|
5681
3856
|
|
|
5682
|
-
|
|
3857
|
+
MixpanelPersistence.prototype._add_to_people_queue = function(queue, data) {
|
|
3858
|
+
var q_key = this._get_queue_key(queue),
|
|
3859
|
+
q_data = data[queue],
|
|
3860
|
+
set_q = this._get_or_create_queue(SET_ACTION),
|
|
3861
|
+
set_once_q = this._get_or_create_queue(SET_ONCE_ACTION),
|
|
3862
|
+
unset_q = this._get_or_create_queue(UNSET_ACTION),
|
|
3863
|
+
add_q = this._get_or_create_queue(ADD_ACTION),
|
|
3864
|
+
union_q = this._get_or_create_queue(UNION_ACTION),
|
|
3865
|
+
remove_q = this._get_or_create_queue(REMOVE_ACTION, []),
|
|
3866
|
+
append_q = this._get_or_create_queue(APPEND_ACTION, []);
|
|
5683
3867
|
|
|
5684
|
-
if (
|
|
5685
|
-
|
|
5686
|
-
|
|
5687
|
-
|
|
5688
|
-
|
|
3868
|
+
if (q_key === SET_QUEUE_KEY) {
|
|
3869
|
+
// Update the set queue - we can override any existing values
|
|
3870
|
+
_.extend(set_q, q_data);
|
|
3871
|
+
// if there was a pending increment, override it
|
|
3872
|
+
// with the set.
|
|
3873
|
+
this._pop_from_people_queue(ADD_ACTION, q_data);
|
|
3874
|
+
// if there was a pending union, override it
|
|
3875
|
+
// with the set.
|
|
3876
|
+
this._pop_from_people_queue(UNION_ACTION, q_data);
|
|
3877
|
+
this._pop_from_people_queue(UNSET_ACTION, q_data);
|
|
3878
|
+
} else if (q_key === SET_ONCE_QUEUE_KEY) {
|
|
3879
|
+
// only queue the data if there is not already a set_once call for it.
|
|
3880
|
+
_.each(q_data, function(v, k) {
|
|
3881
|
+
if (!(k in set_once_q)) {
|
|
3882
|
+
set_once_q[k] = v;
|
|
3883
|
+
}
|
|
3884
|
+
});
|
|
3885
|
+
this._pop_from_people_queue(UNSET_ACTION, q_data);
|
|
3886
|
+
} else if (q_key === UNSET_QUEUE_KEY) {
|
|
3887
|
+
_.each(q_data, function(prop) {
|
|
3888
|
+
|
|
3889
|
+
// undo previously-queued actions on this key
|
|
3890
|
+
_.each([set_q, set_once_q, add_q, union_q], function(enqueued_obj) {
|
|
3891
|
+
if (prop in enqueued_obj) {
|
|
3892
|
+
delete enqueued_obj[prop];
|
|
3893
|
+
}
|
|
3894
|
+
});
|
|
3895
|
+
_.each(append_q, function(append_obj) {
|
|
3896
|
+
if (prop in append_obj) {
|
|
3897
|
+
delete append_obj[prop];
|
|
3898
|
+
}
|
|
3899
|
+
});
|
|
3900
|
+
|
|
3901
|
+
unset_q[prop] = true;
|
|
3902
|
+
|
|
3903
|
+
});
|
|
3904
|
+
} else if (q_key === ADD_QUEUE_KEY) {
|
|
3905
|
+
_.each(q_data, function(v, k) {
|
|
3906
|
+
// If it exists in the set queue, increment
|
|
3907
|
+
// the value
|
|
3908
|
+
if (k in set_q) {
|
|
3909
|
+
set_q[k] += v;
|
|
5689
3910
|
} else {
|
|
5690
|
-
|
|
3911
|
+
// If it doesn't exist, update the add
|
|
3912
|
+
// queue
|
|
3913
|
+
if (!(k in add_q)) {
|
|
3914
|
+
add_q[k] = 0;
|
|
3915
|
+
}
|
|
3916
|
+
add_q[k] += v;
|
|
5691
3917
|
}
|
|
5692
|
-
}
|
|
5693
|
-
|
|
3918
|
+
}, this);
|
|
3919
|
+
this._pop_from_people_queue(UNSET_ACTION, q_data);
|
|
3920
|
+
} else if (q_key === UNION_QUEUE_KEY) {
|
|
3921
|
+
_.each(q_data, function(v, k) {
|
|
3922
|
+
if (_.isArray(v)) {
|
|
3923
|
+
if (!(k in union_q)) {
|
|
3924
|
+
union_q[k] = [];
|
|
3925
|
+
}
|
|
3926
|
+
// We may send duplicates, the server will dedup them.
|
|
3927
|
+
union_q[k] = union_q[k].concat(v);
|
|
3928
|
+
}
|
|
3929
|
+
});
|
|
3930
|
+
this._pop_from_people_queue(UNSET_ACTION, q_data);
|
|
3931
|
+
} else if (q_key === REMOVE_QUEUE_KEY) {
|
|
3932
|
+
remove_q.push(q_data);
|
|
3933
|
+
this._pop_from_people_queue(APPEND_ACTION, q_data);
|
|
3934
|
+
} else if (q_key === APPEND_QUEUE_KEY) {
|
|
3935
|
+
append_q.push(q_data);
|
|
3936
|
+
this._pop_from_people_queue(UNSET_ACTION, q_data);
|
|
5694
3937
|
}
|
|
5695
3938
|
|
|
5696
|
-
|
|
5697
|
-
|
|
5698
|
-
data: date_encoded_data,
|
|
5699
|
-
endpoint: this._get_config('api_host') + '/engage/',
|
|
5700
|
-
batcher: this._mixpanel.request_batchers.people
|
|
5701
|
-
}, callback);
|
|
5702
|
-
};
|
|
3939
|
+
console.log('MIXPANEL PEOPLE REQUEST (QUEUED, PENDING IDENTIFY):');
|
|
3940
|
+
console.log(data);
|
|
5703
3941
|
|
|
5704
|
-
|
|
5705
|
-
return this._mixpanel.get_config(conf_var);
|
|
3942
|
+
this.save();
|
|
5706
3943
|
};
|
|
5707
3944
|
|
|
5708
|
-
|
|
5709
|
-
|
|
5710
|
-
|
|
3945
|
+
MixpanelPersistence.prototype._pop_from_people_queue = function(queue, data) {
|
|
3946
|
+
var q = this._get_queue(queue);
|
|
3947
|
+
if (!_.isUndefined(q)) {
|
|
3948
|
+
_.each(data, function(v, k) {
|
|
3949
|
+
if (queue === APPEND_ACTION || queue === REMOVE_ACTION) {
|
|
3950
|
+
// list actions: only remove if both k+v match
|
|
3951
|
+
// e.g. remove should not override append in a case like
|
|
3952
|
+
// append({foo: 'bar'}); remove({foo: 'qux'})
|
|
3953
|
+
_.each(q, function(queued_action) {
|
|
3954
|
+
if (queued_action[k] === v) {
|
|
3955
|
+
delete queued_action[k];
|
|
3956
|
+
}
|
|
3957
|
+
});
|
|
3958
|
+
} else {
|
|
3959
|
+
delete q[k];
|
|
3960
|
+
}
|
|
3961
|
+
}, this);
|
|
5711
3962
|
|
|
5712
|
-
|
|
5713
|
-
MixpanelPeople.prototype._enqueue = function(data) {
|
|
5714
|
-
if (SET_ACTION in data) {
|
|
5715
|
-
this._mixpanel['persistence']._add_to_people_queue(SET_ACTION, data);
|
|
5716
|
-
} else if (SET_ONCE_ACTION in data) {
|
|
5717
|
-
this._mixpanel['persistence']._add_to_people_queue(SET_ONCE_ACTION, data);
|
|
5718
|
-
} else if (UNSET_ACTION in data) {
|
|
5719
|
-
this._mixpanel['persistence']._add_to_people_queue(UNSET_ACTION, data);
|
|
5720
|
-
} else if (ADD_ACTION in data) {
|
|
5721
|
-
this._mixpanel['persistence']._add_to_people_queue(ADD_ACTION, data);
|
|
5722
|
-
} else if (APPEND_ACTION in data) {
|
|
5723
|
-
this._mixpanel['persistence']._add_to_people_queue(APPEND_ACTION, data);
|
|
5724
|
-
} else if (REMOVE_ACTION in data) {
|
|
5725
|
-
this._mixpanel['persistence']._add_to_people_queue(REMOVE_ACTION, data);
|
|
5726
|
-
} else if (UNION_ACTION in data) {
|
|
5727
|
-
this._mixpanel['persistence']._add_to_people_queue(UNION_ACTION, data);
|
|
5728
|
-
} else {
|
|
5729
|
-
console.error('Invalid call to _enqueue():', data);
|
|
3963
|
+
this.save();
|
|
5730
3964
|
}
|
|
5731
3965
|
};
|
|
5732
3966
|
|
|
5733
|
-
|
|
5734
|
-
|
|
5735
|
-
|
|
5736
|
-
|
|
5737
|
-
|
|
5738
|
-
|
|
5739
|
-
|
|
5740
|
-
|
|
5741
|
-
|
|
5742
|
-
|
|
5743
|
-
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
|
|
5747
|
-
|
|
5748
|
-
|
|
5749
|
-
|
|
5750
|
-
}
|
|
5751
|
-
});
|
|
3967
|
+
MixpanelPersistence.prototype._get_queue_key = function(queue) {
|
|
3968
|
+
if (queue === SET_ACTION) {
|
|
3969
|
+
return SET_QUEUE_KEY;
|
|
3970
|
+
} else if (queue === SET_ONCE_ACTION) {
|
|
3971
|
+
return SET_ONCE_QUEUE_KEY;
|
|
3972
|
+
} else if (queue === UNSET_ACTION) {
|
|
3973
|
+
return UNSET_QUEUE_KEY;
|
|
3974
|
+
} else if (queue === ADD_ACTION) {
|
|
3975
|
+
return ADD_QUEUE_KEY;
|
|
3976
|
+
} else if (queue === APPEND_ACTION) {
|
|
3977
|
+
return APPEND_QUEUE_KEY;
|
|
3978
|
+
} else if (queue === REMOVE_ACTION) {
|
|
3979
|
+
return REMOVE_QUEUE_KEY;
|
|
3980
|
+
} else if (queue === UNION_ACTION) {
|
|
3981
|
+
return UNION_QUEUE_KEY;
|
|
3982
|
+
} else {
|
|
3983
|
+
console.error('Invalid queue:', queue);
|
|
5752
3984
|
}
|
|
5753
3985
|
};
|
|
5754
3986
|
|
|
5755
|
-
|
|
5756
|
-
|
|
5757
|
-
|
|
5758
|
-
|
|
5759
|
-
|
|
5760
|
-
|
|
5761
|
-
var $append_queue = this._mixpanel['persistence']._get_queue(APPEND_ACTION);
|
|
5762
|
-
var $remove_queue = this._mixpanel['persistence']._get_queue(REMOVE_ACTION);
|
|
5763
|
-
|
|
5764
|
-
this._flush_one_queue(SET_ACTION, this.set, _set_callback);
|
|
5765
|
-
this._flush_one_queue(SET_ONCE_ACTION, this.set_once, _set_once_callback);
|
|
5766
|
-
this._flush_one_queue(UNSET_ACTION, this.unset, _unset_callback, function(queue) { return _.keys(queue); });
|
|
5767
|
-
this._flush_one_queue(ADD_ACTION, this.increment, _add_callback);
|
|
5768
|
-
this._flush_one_queue(UNION_ACTION, this.union, _union_callback);
|
|
5769
|
-
|
|
5770
|
-
// we have to fire off each $append individually since there is
|
|
5771
|
-
// no concat method server side
|
|
5772
|
-
if (!_.isUndefined($append_queue) && _.isArray($append_queue) && $append_queue.length) {
|
|
5773
|
-
var $append_item;
|
|
5774
|
-
var append_callback = function(response, data) {
|
|
5775
|
-
if (response === 0) {
|
|
5776
|
-
_this._mixpanel['persistence']._add_to_people_queue(APPEND_ACTION, $append_item);
|
|
5777
|
-
}
|
|
5778
|
-
if (!_.isUndefined(_append_callback)) {
|
|
5779
|
-
_append_callback(response, data);
|
|
5780
|
-
}
|
|
5781
|
-
};
|
|
5782
|
-
for (var i = $append_queue.length - 1; i >= 0; i--) {
|
|
5783
|
-
$append_item = $append_queue.pop();
|
|
5784
|
-
if (!_.isEmptyObject($append_item)) {
|
|
5785
|
-
_this.append($append_item, append_callback);
|
|
5786
|
-
}
|
|
5787
|
-
}
|
|
5788
|
-
// Save the shortened append queue
|
|
5789
|
-
_this._mixpanel['persistence'].save();
|
|
5790
|
-
}
|
|
3987
|
+
MixpanelPersistence.prototype._get_queue = function(queue) {
|
|
3988
|
+
return this['props'][this._get_queue_key(queue)];
|
|
3989
|
+
};
|
|
3990
|
+
MixpanelPersistence.prototype._get_or_create_queue = function(queue, default_val) {
|
|
3991
|
+
var key = this._get_queue_key(queue);
|
|
3992
|
+
default_val = _.isUndefined(default_val) ? {} : default_val;
|
|
5791
3993
|
|
|
5792
|
-
|
|
5793
|
-
if (!_.isUndefined($remove_queue) && _.isArray($remove_queue) && $remove_queue.length) {
|
|
5794
|
-
var $remove_item;
|
|
5795
|
-
var remove_callback = function(response, data) {
|
|
5796
|
-
if (response === 0) {
|
|
5797
|
-
_this._mixpanel['persistence']._add_to_people_queue(REMOVE_ACTION, $remove_item);
|
|
5798
|
-
}
|
|
5799
|
-
if (!_.isUndefined(_remove_callback)) {
|
|
5800
|
-
_remove_callback(response, data);
|
|
5801
|
-
}
|
|
5802
|
-
};
|
|
5803
|
-
for (var j = $remove_queue.length - 1; j >= 0; j--) {
|
|
5804
|
-
$remove_item = $remove_queue.pop();
|
|
5805
|
-
if (!_.isEmptyObject($remove_item)) {
|
|
5806
|
-
_this.remove($remove_item, remove_callback);
|
|
5807
|
-
}
|
|
5808
|
-
}
|
|
5809
|
-
_this._mixpanel['persistence'].save();
|
|
5810
|
-
}
|
|
3994
|
+
return this['props'][key] || (this['props'][key] = default_val);
|
|
5811
3995
|
};
|
|
5812
3996
|
|
|
5813
|
-
|
|
5814
|
-
|
|
3997
|
+
MixpanelPersistence.prototype.set_event_timer = function(event_name, timestamp) {
|
|
3998
|
+
var timers = this['props'][EVENT_TIMERS_KEY] || {};
|
|
3999
|
+
timers[event_name] = timestamp;
|
|
4000
|
+
this['props'][EVENT_TIMERS_KEY] = timers;
|
|
4001
|
+
this.save();
|
|
5815
4002
|
};
|
|
5816
4003
|
|
|
5817
|
-
|
|
5818
|
-
|
|
5819
|
-
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
|
|
5824
|
-
|
|
5825
|
-
|
|
5826
|
-
MixpanelPeople.prototype['clear_charges'] = MixpanelPeople.prototype.clear_charges;
|
|
5827
|
-
MixpanelPeople.prototype['delete_user'] = MixpanelPeople.prototype.delete_user;
|
|
5828
|
-
MixpanelPeople.prototype['toString'] = MixpanelPeople.prototype.toString;
|
|
4004
|
+
MixpanelPersistence.prototype.remove_event_timer = function(event_name) {
|
|
4005
|
+
var timers = this['props'][EVENT_TIMERS_KEY] || {};
|
|
4006
|
+
var timestamp = timers[event_name];
|
|
4007
|
+
if (!_.isUndefined(timestamp)) {
|
|
4008
|
+
delete this['props'][EVENT_TIMERS_KEY][event_name];
|
|
4009
|
+
this.save();
|
|
4010
|
+
}
|
|
4011
|
+
return timestamp;
|
|
4012
|
+
};
|
|
5829
4013
|
|
|
5830
4014
|
/*
|
|
5831
4015
|
* Mixpanel JS Library
|
|
@@ -5863,6 +4047,8 @@
|
|
|
5863
4047
|
var NOOP_FUNC = function() {};
|
|
5864
4048
|
|
|
5865
4049
|
/** @const */ var PRIMARY_INSTANCE_NAME = 'mixpanel';
|
|
4050
|
+
/** @const */ var PAYLOAD_TYPE_BASE64 = 'base64';
|
|
4051
|
+
/** @const */ var PAYLOAD_TYPE_JSON = 'json';
|
|
5866
4052
|
|
|
5867
4053
|
|
|
5868
4054
|
/*
|
|
@@ -5879,10 +4065,10 @@
|
|
|
5879
4065
|
|
|
5880
4066
|
// save reference to navigator.sendBeacon so it can be minified
|
|
5881
4067
|
var sendBeacon = null;
|
|
5882
|
-
if (navigator
|
|
4068
|
+
if (navigator['sendBeacon']) {
|
|
5883
4069
|
sendBeacon = function() {
|
|
5884
4070
|
// late reference to navigator.sendBeacon to allow patching/spying
|
|
5885
|
-
return navigator
|
|
4071
|
+
return navigator['sendBeacon'].apply(navigator, arguments);
|
|
5886
4072
|
};
|
|
5887
4073
|
}
|
|
5888
4074
|
|
|
@@ -5893,10 +4079,12 @@
|
|
|
5893
4079
|
'api_host': 'https://api-js.mixpanel.com',
|
|
5894
4080
|
'api_method': 'POST',
|
|
5895
4081
|
'api_transport': 'XHR',
|
|
4082
|
+
'api_payload_format': PAYLOAD_TYPE_BASE64,
|
|
5896
4083
|
'app_host': 'https://mixpanel.com',
|
|
5897
4084
|
'cdn': 'https://cdn.mxpnl.com',
|
|
5898
4085
|
'cross_site_cookie': false,
|
|
5899
4086
|
'cross_subdomain_cookie': true,
|
|
4087
|
+
'error_reporter': NOOP_FUNC,
|
|
5900
4088
|
'persistence': 'cookie',
|
|
5901
4089
|
'persistence_name': '',
|
|
5902
4090
|
'cookie_domain': '',
|
|
@@ -5921,10 +4109,8 @@
|
|
|
5921
4109
|
'opt_out_tracking_cookie_prefix': null,
|
|
5922
4110
|
'property_blacklist': [],
|
|
5923
4111
|
'xhr_headers': {}, // { header: value, header2: value }
|
|
5924
|
-
'inapp_protocol': '//',
|
|
5925
|
-
'inapp_link_new_window': false,
|
|
5926
4112
|
'ignore_dnt': false,
|
|
5927
|
-
'batch_requests':
|
|
4113
|
+
'batch_requests': true,
|
|
5928
4114
|
'batch_size': 50,
|
|
5929
4115
|
'batch_flush_interval_ms': 5000,
|
|
5930
4116
|
'batch_request_timeout_ms': 90000,
|
|
@@ -5964,8 +4150,6 @@
|
|
|
5964
4150
|
}
|
|
5965
4151
|
|
|
5966
4152
|
instance._cached_groups = {}; // cache groups in a pool
|
|
5967
|
-
instance._user_decide_check_complete = false;
|
|
5968
|
-
instance._events_tracked_before_user_decide_check_complete = [];
|
|
5969
4153
|
|
|
5970
4154
|
instance._init(token, config, name);
|
|
5971
4155
|
|
|
@@ -5988,12 +4172,6 @@
|
|
|
5988
4172
|
return instance;
|
|
5989
4173
|
};
|
|
5990
4174
|
|
|
5991
|
-
var encode_data_for_request = function(data) {
|
|
5992
|
-
var json_data = _.JSONEncode(data);
|
|
5993
|
-
var encoded_data = _.base64Encode(json_data);
|
|
5994
|
-
return {'data': encoded_data};
|
|
5995
|
-
};
|
|
5996
|
-
|
|
5997
4175
|
// Initialization methods
|
|
5998
4176
|
|
|
5999
4177
|
/**
|
|
@@ -6014,11 +4192,11 @@
|
|
|
6014
4192
|
*/
|
|
6015
4193
|
MixpanelLib.prototype.init = function (token, config, name) {
|
|
6016
4194
|
if (_.isUndefined(name)) {
|
|
6017
|
-
|
|
4195
|
+
this.report_error('You must name your new library: init(token, config, name)');
|
|
6018
4196
|
return;
|
|
6019
4197
|
}
|
|
6020
4198
|
if (name === PRIMARY_INSTANCE_NAME) {
|
|
6021
|
-
|
|
4199
|
+
this.report_error('You must initialize the main mixpanel object right after you include the Mixpanel js snippet');
|
|
6022
4200
|
return;
|
|
6023
4201
|
}
|
|
6024
4202
|
|
|
@@ -6041,16 +4219,15 @@
|
|
|
6041
4219
|
|
|
6042
4220
|
this['__loaded'] = true;
|
|
6043
4221
|
this['config'] = {};
|
|
6044
|
-
this['_triggered_notifs'] = [];
|
|
6045
4222
|
|
|
6046
|
-
// rollout: enable batch_requests by default for 60% of projects
|
|
6047
|
-
// (only if they have not specified a value in their init config
|
|
6048
|
-
// and they aren't using a custom API host)
|
|
6049
4223
|
var variable_features = {};
|
|
6050
|
-
|
|
6051
|
-
|
|
6052
|
-
if (!('
|
|
6053
|
-
|
|
4224
|
+
|
|
4225
|
+
// default to JSON payload for standard mixpanel.com API hosts
|
|
4226
|
+
if (!('api_payload_format' in config)) {
|
|
4227
|
+
var api_host = config['api_host'] || DEFAULT_CONFIG['api_host'];
|
|
4228
|
+
if (api_host.match(/\.mixpanel\.com$/)) {
|
|
4229
|
+
variable_features['api_payload_format'] = PAYLOAD_TYPE_JSON;
|
|
4230
|
+
}
|
|
6054
4231
|
}
|
|
6055
4232
|
|
|
6056
4233
|
this.set_config(_.extend({}, DEFAULT_CONFIG, variable_features, config, {
|
|
@@ -6079,15 +4256,32 @@
|
|
|
6079
4256
|
} else {
|
|
6080
4257
|
this.init_batchers();
|
|
6081
4258
|
if (sendBeacon && window$1.addEventListener) {
|
|
6082
|
-
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
|
|
6086
|
-
|
|
4259
|
+
// Before page closes or hides (user tabs away etc), attempt to flush any events
|
|
4260
|
+
// queued up via navigator.sendBeacon. Since sendBeacon doesn't report success/failure,
|
|
4261
|
+
// events will not be removed from the persistent store; if the site is loaded again,
|
|
4262
|
+
// the events will be flushed again on startup and deduplicated on the Mixpanel server
|
|
4263
|
+
// side.
|
|
4264
|
+
// There is no reliable way to capture only page close events, so we lean on the
|
|
4265
|
+
// visibilitychange and pagehide events as recommended at
|
|
4266
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/Window/unload_event#usage_notes.
|
|
4267
|
+
// These events fire when the user clicks away from the current page/tab, so will occur
|
|
4268
|
+
// more frequently than page unload, but are the only mechanism currently for capturing
|
|
4269
|
+
// this scenario somewhat reliably.
|
|
4270
|
+
var flush_on_unload = _.bind(function() {
|
|
6087
4271
|
if (!this.request_batchers.events.stopped) {
|
|
6088
4272
|
this.request_batchers.events.flush({unloading: true});
|
|
6089
4273
|
}
|
|
6090
|
-
}, this)
|
|
4274
|
+
}, this);
|
|
4275
|
+
window$1.addEventListener('pagehide', function(ev) {
|
|
4276
|
+
if (ev['persisted']) {
|
|
4277
|
+
flush_on_unload();
|
|
4278
|
+
}
|
|
4279
|
+
});
|
|
4280
|
+
window$1.addEventListener('visibilitychange', function() {
|
|
4281
|
+
if (document$1['visibilityState'] === 'hidden') {
|
|
4282
|
+
flush_on_unload();
|
|
4283
|
+
}
|
|
4284
|
+
});
|
|
6091
4285
|
}
|
|
6092
4286
|
}
|
|
6093
4287
|
}
|
|
@@ -6143,7 +4337,7 @@
|
|
|
6143
4337
|
|
|
6144
4338
|
MixpanelLib.prototype._track_dom = function(DomClass, args) {
|
|
6145
4339
|
if (this.get_config('img')) {
|
|
6146
|
-
|
|
4340
|
+
this.report_error('You can\'t use DOM tracking functions with img = true.');
|
|
6147
4341
|
return false;
|
|
6148
4342
|
}
|
|
6149
4343
|
|
|
@@ -6245,6 +4439,7 @@
|
|
|
6245
4439
|
|
|
6246
4440
|
url += '?' + _.HTTPBuildQuery(data);
|
|
6247
4441
|
|
|
4442
|
+
var lib = this;
|
|
6248
4443
|
if ('img' in data) {
|
|
6249
4444
|
var img = document$1.createElement('img');
|
|
6250
4445
|
img.src = url;
|
|
@@ -6253,7 +4448,7 @@
|
|
|
6253
4448
|
try {
|
|
6254
4449
|
succeeded = sendBeacon(url, body_data);
|
|
6255
4450
|
} catch (e) {
|
|
6256
|
-
|
|
4451
|
+
lib.report_error(e);
|
|
6257
4452
|
succeeded = false;
|
|
6258
4453
|
}
|
|
6259
4454
|
try {
|
|
@@ -6261,7 +4456,7 @@
|
|
|
6261
4456
|
callback(succeeded ? 1 : 0);
|
|
6262
4457
|
}
|
|
6263
4458
|
} catch (e) {
|
|
6264
|
-
|
|
4459
|
+
lib.report_error(e);
|
|
6265
4460
|
}
|
|
6266
4461
|
} else if (USE_XHR) {
|
|
6267
4462
|
try {
|
|
@@ -6293,7 +4488,7 @@
|
|
|
6293
4488
|
try {
|
|
6294
4489
|
response = _.JSONDecode(req.responseText);
|
|
6295
4490
|
} catch (e) {
|
|
6296
|
-
|
|
4491
|
+
lib.report_error(e);
|
|
6297
4492
|
if (options.ignore_json_errors) {
|
|
6298
4493
|
response = req.responseText;
|
|
6299
4494
|
} else {
|
|
@@ -6316,7 +4511,7 @@
|
|
|
6316
4511
|
} else {
|
|
6317
4512
|
error = 'Bad HTTP status: ' + req.status + ' ' + req.statusText;
|
|
6318
4513
|
}
|
|
6319
|
-
|
|
4514
|
+
lib.report_error(error);
|
|
6320
4515
|
if (callback) {
|
|
6321
4516
|
if (verbose_mode) {
|
|
6322
4517
|
callback({status: 0, error: error, xhr_req: req});
|
|
@@ -6329,7 +4524,7 @@
|
|
|
6329
4524
|
};
|
|
6330
4525
|
req.send(body_data);
|
|
6331
4526
|
} catch (e) {
|
|
6332
|
-
|
|
4527
|
+
lib.report_error(e);
|
|
6333
4528
|
succeeded = false;
|
|
6334
4529
|
}
|
|
6335
4530
|
} else {
|
|
@@ -6412,14 +4607,16 @@
|
|
|
6412
4607
|
sendRequestFunc: _.bind(function(data, options, cb) {
|
|
6413
4608
|
this._send_request(
|
|
6414
4609
|
this.get_config('api_host') + attrs.endpoint,
|
|
6415
|
-
|
|
4610
|
+
this._encode_data_for_request(data),
|
|
6416
4611
|
options,
|
|
6417
4612
|
this._prepare_callback(cb, data)
|
|
6418
4613
|
);
|
|
6419
4614
|
}, this),
|
|
6420
4615
|
beforeSendHook: _.bind(function(item) {
|
|
6421
4616
|
return this._run_hook('before_send_' + attrs.type, item);
|
|
6422
|
-
}, this)
|
|
4617
|
+
}, this),
|
|
4618
|
+
errorReporter: this.get_config('error_reporter'),
|
|
4619
|
+
stopAllBatchingFunc: _.bind(this.stop_batch_senders, this)
|
|
6423
4620
|
}
|
|
6424
4621
|
);
|
|
6425
4622
|
}, this);
|
|
@@ -6486,6 +4683,14 @@
|
|
|
6486
4683
|
}
|
|
6487
4684
|
};
|
|
6488
4685
|
|
|
4686
|
+
MixpanelLib.prototype._encode_data_for_request = function(data) {
|
|
4687
|
+
var encoded_data = _.JSONEncode(data);
|
|
4688
|
+
if (this.get_config('api_payload_format') === PAYLOAD_TYPE_BASE64) {
|
|
4689
|
+
encoded_data = _.base64Encode(encoded_data);
|
|
4690
|
+
}
|
|
4691
|
+
return {'data': encoded_data};
|
|
4692
|
+
};
|
|
4693
|
+
|
|
6489
4694
|
// internal method for handling track vs batch-enqueue logic
|
|
6490
4695
|
MixpanelLib.prototype._track_or_batch = function(options, callback) {
|
|
6491
4696
|
var truncated_data = _.truncate(options.data, 255);
|
|
@@ -6505,7 +4710,7 @@
|
|
|
6505
4710
|
console.log(truncated_data);
|
|
6506
4711
|
return this._send_request(
|
|
6507
4712
|
endpoint,
|
|
6508
|
-
|
|
4713
|
+
this._encode_data_for_request(truncated_data),
|
|
6509
4714
|
send_request_options,
|
|
6510
4715
|
this._prepare_callback(callback, truncated_data)
|
|
6511
4716
|
);
|
|
@@ -6568,7 +4773,7 @@
|
|
|
6568
4773
|
}
|
|
6569
4774
|
|
|
6570
4775
|
if (_.isUndefined(event_name)) {
|
|
6571
|
-
|
|
4776
|
+
this.report_error('No event name provided to mixpanel.track');
|
|
6572
4777
|
return;
|
|
6573
4778
|
}
|
|
6574
4779
|
|
|
@@ -6609,7 +4814,7 @@
|
|
|
6609
4814
|
delete properties[blacklisted_prop];
|
|
6610
4815
|
});
|
|
6611
4816
|
} else {
|
|
6612
|
-
|
|
4817
|
+
this.report_error('Invalid value for property_blacklist config: ' + property_blacklist);
|
|
6613
4818
|
}
|
|
6614
4819
|
|
|
6615
4820
|
var data = {
|
|
@@ -6625,8 +4830,6 @@
|
|
|
6625
4830
|
send_request_options: options
|
|
6626
4831
|
}, callback);
|
|
6627
4832
|
|
|
6628
|
-
this._check_and_handle_triggered_notifications(data);
|
|
6629
|
-
|
|
6630
4833
|
return ret;
|
|
6631
4834
|
});
|
|
6632
4835
|
|
|
@@ -6854,7 +5057,7 @@
|
|
|
6854
5057
|
*/
|
|
6855
5058
|
MixpanelLib.prototype.time_event = function(event_name) {
|
|
6856
5059
|
if (_.isUndefined(event_name)) {
|
|
6857
|
-
|
|
5060
|
+
this.report_error('No event name provided to mixpanel.time_event');
|
|
6858
5061
|
return;
|
|
6859
5062
|
}
|
|
6860
5063
|
|
|
@@ -7037,7 +5240,6 @@
|
|
|
7037
5240
|
this.unregister(ALIAS_ID_KEY);
|
|
7038
5241
|
this.register({'distinct_id': new_distinct_id});
|
|
7039
5242
|
}
|
|
7040
|
-
this._check_and_handle_notifications(this.get_distinct_id());
|
|
7041
5243
|
this._flags.identify_called = true;
|
|
7042
5244
|
// Flush any queued up people requests
|
|
7043
5245
|
this['people']._flush(_set_callback, _add_callback, _append_callback, _set_once_callback, _union_callback, _unset_callback, _remove_callback);
|
|
@@ -7127,7 +5329,7 @@
|
|
|
7127
5329
|
// mixpanel.people.identify() call made for this user. It is VERY BAD to make an alias with
|
|
7128
5330
|
// this ID, as it will duplicate users.
|
|
7129
5331
|
if (alias === this.get_property(PEOPLE_DISTINCT_ID_KEY)) {
|
|
7130
|
-
|
|
5332
|
+
this.report_error('Attempting to create alias for existing People user - aborting.');
|
|
7131
5333
|
return -2;
|
|
7132
5334
|
}
|
|
7133
5335
|
|
|
@@ -7147,7 +5349,7 @@
|
|
|
7147
5349
|
_this.identify(alias);
|
|
7148
5350
|
});
|
|
7149
5351
|
} else {
|
|
7150
|
-
|
|
5352
|
+
this.report_error('alias matches current distinct_id - skipping api call.');
|
|
7151
5353
|
this.identify(alias);
|
|
7152
5354
|
return -1;
|
|
7153
5355
|
}
|
|
@@ -7274,14 +5476,6 @@
|
|
|
7274
5476
|
* // the format {'Header-Name': value}
|
|
7275
5477
|
* xhr_headers: {}
|
|
7276
5478
|
*
|
|
7277
|
-
* // protocol for fetching in-app message resources, e.g.
|
|
7278
|
-
* // 'https://' or 'http://'; defaults to '//' (which defers to the
|
|
7279
|
-
* // current page's protocol)
|
|
7280
|
-
* inapp_protocol: '//'
|
|
7281
|
-
*
|
|
7282
|
-
* // whether to open in-app message link in new tab/window
|
|
7283
|
-
* inapp_link_new_window: false
|
|
7284
|
-
*
|
|
7285
5479
|
* // whether to ignore or respect the web browser's Do Not Track setting
|
|
7286
5480
|
* ignore_dnt: false
|
|
7287
5481
|
* }
|
|
@@ -7330,7 +5524,7 @@
|
|
|
7330
5524
|
MixpanelLib.prototype._run_hook = function(hook_name) {
|
|
7331
5525
|
var ret = (this['config']['hooks'][hook_name] || IDENTITY_FUNC).apply(this, slice.call(arguments, 1));
|
|
7332
5526
|
if (typeof ret === 'undefined') {
|
|
7333
|
-
|
|
5527
|
+
this.report_error(hook_name + ' hook did not return a value');
|
|
7334
5528
|
ret = null;
|
|
7335
5529
|
}
|
|
7336
5530
|
return ret;
|
|
@@ -7372,75 +5566,6 @@
|
|
|
7372
5566
|
_.include(this.__disabled_events, event_name);
|
|
7373
5567
|
};
|
|
7374
5568
|
|
|
7375
|
-
MixpanelLib.prototype._check_and_handle_triggered_notifications = addOptOutCheckMixpanelLib(function(event_data) {
|
|
7376
|
-
if (!this._user_decide_check_complete) {
|
|
7377
|
-
this._events_tracked_before_user_decide_check_complete.push(event_data);
|
|
7378
|
-
} else {
|
|
7379
|
-
var arr = this['_triggered_notifs'];
|
|
7380
|
-
for (var i = 0; i < arr.length; i++) {
|
|
7381
|
-
var notif = new MixpanelNotification(arr[i], this);
|
|
7382
|
-
if (notif._matches_event_data(event_data)) {
|
|
7383
|
-
this._show_notification(arr[i]);
|
|
7384
|
-
return;
|
|
7385
|
-
}
|
|
7386
|
-
}
|
|
7387
|
-
}
|
|
7388
|
-
});
|
|
7389
|
-
|
|
7390
|
-
MixpanelLib.prototype._check_and_handle_notifications = addOptOutCheckMixpanelLib(function(distinct_id) {
|
|
7391
|
-
if (
|
|
7392
|
-
!distinct_id ||
|
|
7393
|
-
this._flags.identify_called ||
|
|
7394
|
-
this.get_config('disable_notifications')
|
|
7395
|
-
) {
|
|
7396
|
-
return;
|
|
7397
|
-
}
|
|
7398
|
-
|
|
7399
|
-
console.log('MIXPANEL NOTIFICATION CHECK');
|
|
7400
|
-
|
|
7401
|
-
var data = {
|
|
7402
|
-
'verbose': true,
|
|
7403
|
-
'version': '3',
|
|
7404
|
-
'lib': 'web',
|
|
7405
|
-
'token': this.get_config('token'),
|
|
7406
|
-
'distinct_id': distinct_id
|
|
7407
|
-
};
|
|
7408
|
-
this._send_request(
|
|
7409
|
-
this.get_config('api_host') + '/decide/',
|
|
7410
|
-
data,
|
|
7411
|
-
{method: 'GET', transport: 'XHR'},
|
|
7412
|
-
this._prepare_callback(_.bind(function(result) {
|
|
7413
|
-
if (result['notifications'] && result['notifications'].length > 0) {
|
|
7414
|
-
this['_triggered_notifs'] = [];
|
|
7415
|
-
var notifications = [];
|
|
7416
|
-
_.each(result['notifications'], function(notif) {
|
|
7417
|
-
(notif['display_triggers'] && notif['display_triggers'].length > 0 ? this['_triggered_notifs'] : notifications).push(notif);
|
|
7418
|
-
}, this);
|
|
7419
|
-
if (notifications.length > 0) {
|
|
7420
|
-
this._show_notification.call(this, notifications[0]);
|
|
7421
|
-
}
|
|
7422
|
-
}
|
|
7423
|
-
this._handle_user_decide_check_complete();
|
|
7424
|
-
}, this))
|
|
7425
|
-
);
|
|
7426
|
-
});
|
|
7427
|
-
|
|
7428
|
-
MixpanelLib.prototype._handle_user_decide_check_complete = function() {
|
|
7429
|
-
this._user_decide_check_complete = true;
|
|
7430
|
-
|
|
7431
|
-
// check notifications against events that were tracked before decide call completed
|
|
7432
|
-
var events = this._events_tracked_before_user_decide_check_complete;
|
|
7433
|
-
while (events.length > 0) {
|
|
7434
|
-
var data = events.shift(); // replay in the same order they came in
|
|
7435
|
-
this._check_and_handle_triggered_notifications(data);
|
|
7436
|
-
}
|
|
7437
|
-
};
|
|
7438
|
-
|
|
7439
|
-
MixpanelLib.prototype._show_notification = function(notif_data) {
|
|
7440
|
-
var notification = new MixpanelNotification(notif_data, this);
|
|
7441
|
-
notification.show();
|
|
7442
|
-
};
|
|
7443
|
-
|
|
7444
5569
|
// perform some housekeeping around GDPR opt-in/out state
|
|
7445
5570
|
MixpanelLib.prototype._gdpr_init = function() {
|
|
7446
5571
|
var is_localStorage_requested = this.get_config('opt_out_tracking_persistence_type') === 'localStorage';
|
|
@@ -7686,6 +5811,18 @@
|
|
|
7686
5811
|
this._gdpr_update_persistence(options);
|
|
7687
5812
|
};
|
|
7688
5813
|
|
|
5814
|
+
MixpanelLib.prototype.report_error = function(msg, err) {
|
|
5815
|
+
console.error.apply(console.error, arguments);
|
|
5816
|
+
try {
|
|
5817
|
+
if (!err && !(msg instanceof Error)) {
|
|
5818
|
+
msg = new Error(msg);
|
|
5819
|
+
}
|
|
5820
|
+
this.get_config('error_reporter')(msg, err);
|
|
5821
|
+
} catch(err) {
|
|
5822
|
+
console.error(err);
|
|
5823
|
+
}
|
|
5824
|
+
};
|
|
5825
|
+
|
|
7689
5826
|
// EXPORTS (for closure compiler)
|
|
7690
5827
|
|
|
7691
5828
|
// MixpanelLib Exports
|
|
@@ -7708,9 +5845,6 @@
|
|
|
7708
5845
|
MixpanelLib.prototype['get_property'] = MixpanelLib.prototype.get_property;
|
|
7709
5846
|
MixpanelLib.prototype['get_distinct_id'] = MixpanelLib.prototype.get_distinct_id;
|
|
7710
5847
|
MixpanelLib.prototype['toString'] = MixpanelLib.prototype.toString;
|
|
7711
|
-
MixpanelLib.prototype['_check_and_handle_notifications'] = MixpanelLib.prototype._check_and_handle_notifications;
|
|
7712
|
-
MixpanelLib.prototype['_handle_user_decide_check_complete'] = MixpanelLib.prototype._handle_user_decide_check_complete;
|
|
7713
|
-
MixpanelLib.prototype['_show_notification'] = MixpanelLib.prototype._show_notification;
|
|
7714
5848
|
MixpanelLib.prototype['opt_out_tracking'] = MixpanelLib.prototype.opt_out_tracking;
|
|
7715
5849
|
MixpanelLib.prototype['opt_in_tracking'] = MixpanelLib.prototype.opt_in_tracking;
|
|
7716
5850
|
MixpanelLib.prototype['has_opted_out_tracking'] = MixpanelLib.prototype.has_opted_out_tracking;
|
|
@@ -7731,8 +5865,6 @@
|
|
|
7731
5865
|
MixpanelPersistence.prototype['get_cross_subdomain'] = MixpanelPersistence.prototype.get_cross_subdomain;
|
|
7732
5866
|
MixpanelPersistence.prototype['clear'] = MixpanelPersistence.prototype.clear;
|
|
7733
5867
|
|
|
7734
|
-
_.safewrap_class(MixpanelLib, ['identify', '_check_and_handle_notifications', '_show_notification']);
|
|
7735
|
-
|
|
7736
5868
|
|
|
7737
5869
|
var instances = {};
|
|
7738
5870
|
var extend_mp = function() {
|