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