mixpanel-browser 2.72.0 → 2.73.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.
@@ -21,7 +21,9 @@
21
21
  screen: { width: 0, height: 0 },
22
22
  location: loc,
23
23
  addEventListener: function() {},
24
- removeEventListener: function() {}
24
+ removeEventListener: function() {},
25
+ dispatchEvent: function() {},
26
+ CustomEvent: function () {}
25
27
  };
26
28
  } else {
27
29
  win = window;
@@ -14536,7 +14538,7 @@
14536
14538
 
14537
14539
  var Config = {
14538
14540
  DEBUG: false,
14539
- LIB_VERSION: '2.72.0'
14541
+ LIB_VERSION: '2.73.0'
14540
14542
  };
14541
14543
 
14542
14544
  /* eslint camelcase: "off", eqeqeq: "off" */
@@ -21804,8 +21806,6 @@
21804
21806
  var INIT_MODULE = 0;
21805
21807
  var INIT_SNIPPET = 1;
21806
21808
 
21807
- var IDENTITY_FUNC = function(x) {return x;};
21808
-
21809
21809
  /** @const */ var PRIMARY_INSTANCE_NAME = 'mixpanel';
21810
21810
  /** @const */ var PAYLOAD_TYPE_BASE64 = 'base64';
21811
21811
  /** @const */ var PAYLOAD_TYPE_JSON = 'json';
@@ -21971,6 +21971,17 @@
21971
21971
  // global debug to be true
21972
21972
  Config.DEBUG = Config.DEBUG || instance.get_config('debug');
21973
21973
 
21974
+ var source = init_type === INIT_MODULE ? 'module' : 'snippet';
21975
+ win.dispatchEvent(new win.CustomEvent('$mp_sdk_to_extension_event', {
21976
+ 'detail': {
21977
+ 'instance': instance,
21978
+ 'source': source,
21979
+ 'token': token,
21980
+ 'name': name,
21981
+ 'info': _.info
21982
+ }
21983
+ }));
21984
+
21974
21985
  // if target is not defined, we called init after the lib already
21975
21986
  // loaded, so there won't be an array of things to execute
21976
21987
  if (!_.isUndefined(target) && _.isArray(target)) {
@@ -22041,6 +22052,8 @@
22041
22052
  }
22042
22053
  }
22043
22054
 
22055
+ this.hooks = {};
22056
+
22044
22057
  this.set_config(_.extend({}, DEFAULT_CONFIG, variable_features, config, {
22045
22058
  'name': name,
22046
22059
  'token': token,
@@ -22641,7 +22654,12 @@
22641
22654
  );
22642
22655
  }, this),
22643
22656
  beforeSendHook: _.bind(function(item) {
22644
- return this._run_hook('before_send_' + attrs.type, item);
22657
+ var ret = this._run_hook('before_send_' + attrs.type, item);
22658
+ if (ret) {
22659
+ return ret[0];
22660
+ } else {
22661
+ return null;
22662
+ }
22645
22663
  }, this),
22646
22664
  stopAllBatchingFunc: _.bind(this.stop_batch_senders, this),
22647
22665
  usePersistence: true,
@@ -22734,6 +22752,9 @@
22734
22752
  var send_request_immediately = _.bind(function() {
22735
22753
  if (!send_request_options.skip_hooks) {
22736
22754
  truncated_data = this._run_hook('before_send_' + options.type, truncated_data);
22755
+ if (truncated_data) {
22756
+ truncated_data = truncated_data[0];
22757
+ }
22737
22758
  }
22738
22759
  if (truncated_data) {
22739
22760
  console$1.log('MIXPANEL REQUEST:');
@@ -22788,6 +22809,17 @@
22788
22809
  * with the tracking payload sent to the API server is returned; otherwise false.
22789
22810
  */
22790
22811
  MixpanelLib.prototype.track = addOptOutCheckMixpanelLib(function(event_name, properties, options, callback) {
22812
+ var ret;
22813
+ if (!(options && options.skip_hooks)) {
22814
+ ret = this._run_hook('before_track', event_name, properties);
22815
+ if (ret === null) {
22816
+ return;
22817
+ } else {
22818
+ event_name = ret[0];
22819
+ properties = ret[1];
22820
+ }
22821
+ }
22822
+
22791
22823
  if (!callback && typeof options === 'function') {
22792
22824
  callback = options;
22793
22825
  options = null;
@@ -22857,7 +22889,7 @@
22857
22889
  'event': event_name,
22858
22890
  'properties': properties
22859
22891
  };
22860
- var ret = this._track_or_batch({
22892
+ ret = this._track_or_batch({
22861
22893
  type: 'events',
22862
22894
  data: data,
22863
22895
  endpoint: this.get_api_host('events') + '/' + this.get_config('api_routes')['track'],
@@ -23203,6 +23235,14 @@
23203
23235
  * @param {boolean} [days_or_options.persistent=true] - whether to put in persistent storage (cookie/localStorage)
23204
23236
  */
23205
23237
  MixpanelLib.prototype.register = function(props, days_or_options) {
23238
+ var ret = this._run_hook('before_register', props, days_or_options);
23239
+ if (ret === null) {
23240
+ return;
23241
+ } else {
23242
+ props = ret[0];
23243
+ days_or_options = ret[1];
23244
+ }
23245
+
23206
23246
  var options = options_for_register(days_or_options);
23207
23247
  if (options['persistent']) {
23208
23248
  this['persistence'].register(props, options['days']);
@@ -23239,6 +23279,15 @@
23239
23279
  * @param {boolean} [days_or_options.persistent=true] - whether to put in persistent storage (cookie/localStorage)
23240
23280
  */
23241
23281
  MixpanelLib.prototype.register_once = function(props, default_value, days_or_options) {
23282
+ var ret = this._run_hook('before_register_once', props, default_value, days_or_options);
23283
+ if (ret === null) {
23284
+ return;
23285
+ } else {
23286
+ props = ret[0];
23287
+ default_value = ret[1];
23288
+ days_or_options = ret[2];
23289
+ }
23290
+
23242
23291
  var options = options_for_register(days_or_options);
23243
23292
  if (options['persistent']) {
23244
23293
  this['persistence'].register_once(props, default_value, options['days']);
@@ -23262,6 +23311,14 @@
23262
23311
  * @param {boolean} [options.persistent=true] - whether to look in persistent storage (cookie/localStorage)
23263
23312
  */
23264
23313
  MixpanelLib.prototype.unregister = function(property, options) {
23314
+ var ret = this._run_hook('before_unregister', property, options);
23315
+ if (ret === null) {
23316
+ return;
23317
+ } else {
23318
+ property = ret[0];
23319
+ options = ret[1];
23320
+ }
23321
+
23265
23322
  options = options_for_register(options);
23266
23323
  if (options['persistent']) {
23267
23324
  this['persistence'].unregister(property);
@@ -23310,6 +23367,13 @@
23310
23367
  // _set_once_callback:function A callback to be run if and when the People set_once queue is flushed
23311
23368
  // _union_callback:function A callback to be run if and when the People union queue is flushed
23312
23369
  // _unset_callback:function A callback to be run if and when the People unset queue is flushed
23370
+ var ret = this._run_hook('before_identify', new_distinct_id);
23371
+
23372
+ if (ret === null) {
23373
+ return -1;
23374
+ } else {
23375
+ new_distinct_id = ret[0];
23376
+ }
23313
23377
 
23314
23378
  var previous_distinct_id = this.get_distinct_id();
23315
23379
  if (new_distinct_id && previous_distinct_id !== new_distinct_id) {
@@ -23634,6 +23698,25 @@
23634
23698
  if (('autocapture' in config || 'record_heatmap_data' in config) && this.autocapture) {
23635
23699
  this.autocapture.init();
23636
23700
  }
23701
+
23702
+ if (_.isObject(config['hooks'])) {
23703
+ this.hooks = {};
23704
+ _.each(config['hooks'], function(hook_value, hook_name) {
23705
+ if (_.isFunction(hook_value)) {
23706
+ this.hooks[hook_name] = [hook_value];
23707
+ } else if (_.isArray(hook_value)) {
23708
+ this.hooks[hook_name] = [];
23709
+ for (var i = 0; i < hook_value.length; i++) {
23710
+ if (!_.isFunction(hook_value[i])) {
23711
+ console$1.critical('Invalid hook added. Hook is not a function');
23712
+ }
23713
+ this.hooks[hook_name].push(hook_value[i]);
23714
+ }
23715
+ } else {
23716
+ console$1.critical('Invalid hooks added. Ensure that the hook values passed into config.hooks are functions or arrays of functions.');
23717
+ }
23718
+ }, this);
23719
+ }
23637
23720
  }
23638
23721
  };
23639
23722
 
@@ -23651,12 +23734,26 @@
23651
23734
  * @returns {any|null} return value of user-provided hook, or null if nothing was returned
23652
23735
  */
23653
23736
  MixpanelLib.prototype._run_hook = function(hook_name) {
23654
- var ret = (this['config']['hooks'][hook_name] || IDENTITY_FUNC).apply(this, slice.call(arguments, 1));
23655
- if (typeof ret === 'undefined') {
23656
- this.report_error(hook_name + ' hook did not return a value');
23657
- ret = null;
23658
- }
23659
- return ret;
23737
+ var hook_data = slice.call(arguments, 1);
23738
+ _.each(this.hooks[hook_name], function(hook) {
23739
+ if (hook_data === null) {
23740
+ return null;
23741
+ }
23742
+
23743
+ var ret = hook.apply(this, hook_data);
23744
+
23745
+ if (typeof ret === 'undefined') {
23746
+ this.report_error(hook_name + ' hook did not return a valid value');
23747
+ hook_data = null;
23748
+ } else {
23749
+ if (!_.isArray(ret)) {
23750
+ ret = [ret];
23751
+ }
23752
+ hook_data.splice.apply(hook_data, [0, ret.length].concat(ret));
23753
+ }
23754
+ }, this);
23755
+
23756
+ return hook_data;
23660
23757
  };
23661
23758
 
23662
23759
  /**
@@ -23967,6 +24064,25 @@
23967
24064
  }
23968
24065
  };
23969
24066
 
24067
+ MixpanelLib.prototype.add_hook = function(hook_name, hook_fn) {
24068
+ if (!this.hooks[hook_name]) {
24069
+ this.hooks[hook_name] = [];
24070
+ }
24071
+ this.hooks[hook_name].push(hook_fn);
24072
+ };
24073
+
24074
+ MixpanelLib.prototype.remove_hook = function(hook_name, hook_fn) {
24075
+ var fn_index;
24076
+ if (this.hooks[hook_name]) {
24077
+ fn_index = this.hooks[hook_name].indexOf(hook_fn);
24078
+ if (fn_index !== -1) {
24079
+ this.hooks[hook_name].splice(fn_index, 1);
24080
+ } else {
24081
+ console$1.log('remove_hook failed. Matching hook was not found');
24082
+ }
24083
+ }
24084
+ };
24085
+
23970
24086
  // EXPORTS (for closure compiler)
23971
24087
 
23972
24088
  // MixpanelLib Exports
@@ -23999,6 +24115,8 @@
23999
24115
  MixpanelLib.prototype['set_group'] = MixpanelLib.prototype.set_group;
24000
24116
  MixpanelLib.prototype['add_group'] = MixpanelLib.prototype.add_group;
24001
24117
  MixpanelLib.prototype['remove_group'] = MixpanelLib.prototype.remove_group;
24118
+ MixpanelLib.prototype['add_hook'] = MixpanelLib.prototype.add_hook;
24119
+ MixpanelLib.prototype['remove_hook'] = MixpanelLib.prototype.remove_hook;
24002
24120
  MixpanelLib.prototype['track_with_groups'] = MixpanelLib.prototype.track_with_groups;
24003
24121
  MixpanelLib.prototype['start_batch_senders'] = MixpanelLib.prototype.start_batch_senders;
24004
24122
  MixpanelLib.prototype['stop_batch_senders'] = MixpanelLib.prototype.stop_batch_senders;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mixpanel-browser",
3
- "version": "2.72.0",
3
+ "version": "2.73.0",
4
4
  "description": "The official Mixpanel JavaScript browser client library",
5
5
  "main": "dist/mixpanel.cjs.js",
6
6
  "module": "dist/mixpanel.module.js",
package/src/config.js CHANGED
@@ -1,6 +1,6 @@
1
1
  var Config = {
2
2
  DEBUG: false,
3
- LIB_VERSION: '2.72.0'
3
+ LIB_VERSION: '2.73.0'
4
4
  };
5
5
 
6
6
  export default Config;
package/src/index.d.ts CHANGED
@@ -2,7 +2,7 @@ export type Persistence = "cookie" | "localStorage";
2
2
 
3
3
  export type ApiPayloadFormat = "base64" | "json";
4
4
 
5
- export type PushItem = Array<string | Dict>;
5
+ export type PushItem = Array<string | Dict | ((this: Mixpanel) => void)>;
6
6
 
7
7
  export type Query = string | Element | Element[];
8
8
 
@@ -155,6 +155,11 @@ export interface FlagsConfig {
155
155
  context: Dict;
156
156
  }
157
157
 
158
+ export interface BeforeSendHookPayload {
159
+ event: string;
160
+ properties: Record<string, any>;
161
+ }
162
+
158
163
  export interface Config {
159
164
  api_host: string;
160
165
  api_routes: {
@@ -167,10 +172,13 @@ export interface Config {
167
172
  app_host: string;
168
173
  api_payload_format: ApiPayloadFormat;
169
174
  autotrack: boolean;
175
+ batch_autostart: boolean;
176
+ batch_requests: boolean;
170
177
  cdn: string;
171
178
  cookie_domain: string;
172
179
  cross_site_cookie: boolean;
173
180
  cross_subdomain_cookie: boolean;
181
+ error_reporter: (msg: string, err?: Error) => void;
174
182
  flags: boolean | FlagsConfig;
175
183
  persistence: Persistence;
176
184
  persistence_name: string;
@@ -207,10 +215,10 @@ export interface Config {
207
215
  inapp_protocol: string;
208
216
  inapp_link_new_window: boolean;
209
217
  ignore_dnt: boolean;
210
- batch_requests: boolean;
211
218
  batch_size: number;
212
219
  batch_flush_interval_ms: number;
213
220
  batch_request_timeout_ms: number;
221
+ recorder_src: string;
214
222
  record_block_class: string | RegExp;
215
223
  record_block_selector: string;
216
224
  record_collect_fonts: boolean;
@@ -223,6 +231,29 @@ export interface Config {
223
231
  record_sessions_percent: number;
224
232
  record_canvas: boolean;
225
233
  record_heatmap_data: boolean;
234
+ hooks: {
235
+ before_identify?: (new_distinct_id: string) => string | null;
236
+ before_register?: (
237
+ props: Dict,
238
+ days_or_options?: number | Partial<RegisterOptions>
239
+ ) => Dict | Array<Dict | number | Partial<RegisterOptions>> | null;
240
+ before_register_once?: (
241
+ props: Dict,
242
+ default_value?: any,
243
+ days_or_options?: number | Partial<RegisterOptions>
244
+ ) => Dict | Array<any | Dict | number | Partial<RegisterOptions>> | null;
245
+ before_send_events?: (
246
+ event: BeforeSendHookPayload
247
+ ) => BeforeSendHookPayload | null;
248
+ before_track?: (
249
+ event_name: string,
250
+ properties: Dict
251
+ ) => string | Array<string | Dict> | null;
252
+ before_unregister?: (
253
+ property: string,
254
+ options?: Partial<RegisterOptions>
255
+ ) => string | Partial<RegisterOptions> | null;
256
+ };
226
257
  }
227
258
 
228
259
  export type VerboseResponse =
@@ -323,10 +354,11 @@ export interface Mixpanel {
323
354
  get_distinct_id(): any;
324
355
  get_group(group_key: string, group_id: string): Group;
325
356
  get_property(property_name: string): any;
357
+ get_session_replay_url(): string;
326
358
  has_opted_in_tracking(options?: Partial<HasOptedInOutOptions>): boolean;
327
359
  has_opted_out_tracking(options?: Partial<HasOptedInOutOptions>): boolean;
328
360
  identify(unique_id?: string): any;
329
- init(token: string, config: Partial<Config>, name: string): Mixpanel;
361
+ init(token: string, config: Partial<Config>, name?: string): Mixpanel;
330
362
  opt_in_tracking(options?: Partial<InTrackingOptions>): void;
331
363
  opt_out_tracking(options?: Partial<OutTrackingOptions>): void;
332
364
  push(item: PushItem): void;
@@ -351,6 +383,7 @@ export interface Mixpanel {
351
383
  group_ids: string | string[] | number | number[],
352
384
  callback?: Callback
353
385
  ): void;
386
+ start_batch_senders(): void;
354
387
  time_event(event_name: string): void;
355
388
  track(
356
389
  event_name: string,
@@ -380,6 +413,7 @@ export interface Mixpanel {
380
413
  ): void;
381
414
  unregister(property: string, options?: Partial<RegisterOptions>): void;
382
415
  people: People;
416
+ start_batch_senders(): void;
383
417
  start_session_recording(): void;
384
418
  stop_session_recording(): void;
385
419
  get_session_recording_properties(): { $mp_replay_id?: string } | {};
@@ -57,8 +57,6 @@ var mixpanel_master; // main mixpanel instance / object
57
57
  var INIT_MODULE = 0;
58
58
  var INIT_SNIPPET = 1;
59
59
 
60
- var IDENTITY_FUNC = function(x) {return x;};
61
-
62
60
  /** @const */ var PRIMARY_INSTANCE_NAME = 'mixpanel';
63
61
  /** @const */ var PAYLOAD_TYPE_BASE64 = 'base64';
64
62
  /** @const */ var PAYLOAD_TYPE_JSON = 'json';
@@ -224,6 +222,17 @@ var create_mplib = function(token, config, name) {
224
222
  // global debug to be true
225
223
  Config.DEBUG = Config.DEBUG || instance.get_config('debug');
226
224
 
225
+ var source = init_type === INIT_MODULE ? 'module' : 'snippet';
226
+ window.dispatchEvent(new window.CustomEvent('$mp_sdk_to_extension_event', {
227
+ 'detail': {
228
+ 'instance': instance,
229
+ 'source': source,
230
+ 'token': token,
231
+ 'name': name,
232
+ 'info': _.info
233
+ }
234
+ }));
235
+
227
236
  // if target is not defined, we called init after the lib already
228
237
  // loaded, so there won't be an array of things to execute
229
238
  if (!_.isUndefined(target) && _.isArray(target)) {
@@ -294,6 +303,8 @@ MixpanelLib.prototype._init = function(token, config, name) {
294
303
  }
295
304
  }
296
305
 
306
+ this.hooks = {};
307
+
297
308
  this.set_config(_.extend({}, DEFAULT_CONFIG, variable_features, config, {
298
309
  'name': name,
299
310
  'token': token,
@@ -894,7 +905,12 @@ MixpanelLib.prototype.init_batchers = function() {
894
905
  );
895
906
  }, this),
896
907
  beforeSendHook: _.bind(function(item) {
897
- return this._run_hook('before_send_' + attrs.type, item);
908
+ var ret = this._run_hook('before_send_' + attrs.type, item);
909
+ if (ret) {
910
+ return ret[0];
911
+ } else {
912
+ return null;
913
+ }
898
914
  }, this),
899
915
  stopAllBatchingFunc: _.bind(this.stop_batch_senders, this),
900
916
  usePersistence: true,
@@ -987,6 +1003,9 @@ MixpanelLib.prototype._track_or_batch = function(options, callback) {
987
1003
  var send_request_immediately = _.bind(function() {
988
1004
  if (!send_request_options.skip_hooks) {
989
1005
  truncated_data = this._run_hook('before_send_' + options.type, truncated_data);
1006
+ if (truncated_data) {
1007
+ truncated_data = truncated_data[0];
1008
+ }
990
1009
  }
991
1010
  if (truncated_data) {
992
1011
  console.log('MIXPANEL REQUEST:');
@@ -1041,6 +1060,17 @@ MixpanelLib.prototype._track_or_batch = function(options, callback) {
1041
1060
  * with the tracking payload sent to the API server is returned; otherwise false.
1042
1061
  */
1043
1062
  MixpanelLib.prototype.track = addOptOutCheckMixpanelLib(function(event_name, properties, options, callback) {
1063
+ var ret;
1064
+ if (!(options && options.skip_hooks)) {
1065
+ ret = this._run_hook('before_track', event_name, properties);
1066
+ if (ret === null) {
1067
+ return;
1068
+ } else {
1069
+ event_name = ret[0];
1070
+ properties = ret[1];
1071
+ }
1072
+ }
1073
+
1044
1074
  if (!callback && typeof options === 'function') {
1045
1075
  callback = options;
1046
1076
  options = null;
@@ -1110,7 +1140,7 @@ MixpanelLib.prototype.track = addOptOutCheckMixpanelLib(function(event_name, pro
1110
1140
  'event': event_name,
1111
1141
  'properties': properties
1112
1142
  };
1113
- var ret = this._track_or_batch({
1143
+ ret = this._track_or_batch({
1114
1144
  type: 'events',
1115
1145
  data: data,
1116
1146
  endpoint: this.get_api_host('events') + '/' + this.get_config('api_routes')['track'],
@@ -1456,6 +1486,14 @@ var options_for_register = function(days_or_options) {
1456
1486
  * @param {boolean} [days_or_options.persistent=true] - whether to put in persistent storage (cookie/localStorage)
1457
1487
  */
1458
1488
  MixpanelLib.prototype.register = function(props, days_or_options) {
1489
+ var ret = this._run_hook('before_register', props, days_or_options);
1490
+ if (ret === null) {
1491
+ return;
1492
+ } else {
1493
+ props = ret[0];
1494
+ days_or_options = ret[1];
1495
+ }
1496
+
1459
1497
  var options = options_for_register(days_or_options);
1460
1498
  if (options['persistent']) {
1461
1499
  this['persistence'].register(props, options['days']);
@@ -1492,6 +1530,15 @@ MixpanelLib.prototype.register = function(props, days_or_options) {
1492
1530
  * @param {boolean} [days_or_options.persistent=true] - whether to put in persistent storage (cookie/localStorage)
1493
1531
  */
1494
1532
  MixpanelLib.prototype.register_once = function(props, default_value, days_or_options) {
1533
+ var ret = this._run_hook('before_register_once', props, default_value, days_or_options);
1534
+ if (ret === null) {
1535
+ return;
1536
+ } else {
1537
+ props = ret[0];
1538
+ default_value = ret[1];
1539
+ days_or_options = ret[2];
1540
+ }
1541
+
1495
1542
  var options = options_for_register(days_or_options);
1496
1543
  if (options['persistent']) {
1497
1544
  this['persistence'].register_once(props, default_value, options['days']);
@@ -1515,6 +1562,14 @@ MixpanelLib.prototype.register_once = function(props, default_value, days_or_opt
1515
1562
  * @param {boolean} [options.persistent=true] - whether to look in persistent storage (cookie/localStorage)
1516
1563
  */
1517
1564
  MixpanelLib.prototype.unregister = function(property, options) {
1565
+ var ret = this._run_hook('before_unregister', property, options);
1566
+ if (ret === null) {
1567
+ return;
1568
+ } else {
1569
+ property = ret[0];
1570
+ options = ret[1];
1571
+ }
1572
+
1518
1573
  options = options_for_register(options);
1519
1574
  if (options['persistent']) {
1520
1575
  this['persistence'].unregister(property);
@@ -1563,6 +1618,13 @@ MixpanelLib.prototype.identify = function(
1563
1618
  // _set_once_callback:function A callback to be run if and when the People set_once queue is flushed
1564
1619
  // _union_callback:function A callback to be run if and when the People union queue is flushed
1565
1620
  // _unset_callback:function A callback to be run if and when the People unset queue is flushed
1621
+ var ret = this._run_hook('before_identify', new_distinct_id);
1622
+
1623
+ if (ret === null) {
1624
+ return -1;
1625
+ } else {
1626
+ new_distinct_id = ret[0];
1627
+ }
1566
1628
 
1567
1629
  var previous_distinct_id = this.get_distinct_id();
1568
1630
  if (new_distinct_id && previous_distinct_id !== new_distinct_id) {
@@ -1887,6 +1949,25 @@ MixpanelLib.prototype.set_config = function(config) {
1887
1949
  if (('autocapture' in config || 'record_heatmap_data' in config) && this.autocapture) {
1888
1950
  this.autocapture.init();
1889
1951
  }
1952
+
1953
+ if (_.isObject(config['hooks'])) {
1954
+ this.hooks = {};
1955
+ _.each(config['hooks'], function(hook_value, hook_name) {
1956
+ if (_.isFunction(hook_value)) {
1957
+ this.hooks[hook_name] = [hook_value];
1958
+ } else if (_.isArray(hook_value)) {
1959
+ this.hooks[hook_name] = [];
1960
+ for (var i = 0; i < hook_value.length; i++) {
1961
+ if (!_.isFunction(hook_value[i])) {
1962
+ console.critical('Invalid hook added. Hook is not a function');
1963
+ }
1964
+ this.hooks[hook_name].push(hook_value[i]);
1965
+ }
1966
+ } else {
1967
+ console.critical('Invalid hooks added. Ensure that the hook values passed into config.hooks are functions or arrays of functions.');
1968
+ }
1969
+ }, this);
1970
+ }
1890
1971
  }
1891
1972
  };
1892
1973
 
@@ -1904,12 +1985,26 @@ MixpanelLib.prototype.get_config = function(prop_name) {
1904
1985
  * @returns {any|null} return value of user-provided hook, or null if nothing was returned
1905
1986
  */
1906
1987
  MixpanelLib.prototype._run_hook = function(hook_name) {
1907
- var ret = (this['config']['hooks'][hook_name] || IDENTITY_FUNC).apply(this, slice.call(arguments, 1));
1908
- if (typeof ret === 'undefined') {
1909
- this.report_error(hook_name + ' hook did not return a value');
1910
- ret = null;
1911
- }
1912
- return ret;
1988
+ var hook_data = slice.call(arguments, 1);
1989
+ _.each(this.hooks[hook_name], function(hook) {
1990
+ if (hook_data === null) {
1991
+ return null;
1992
+ }
1993
+
1994
+ var ret = hook.apply(this, hook_data);
1995
+
1996
+ if (typeof ret === 'undefined') {
1997
+ this.report_error(hook_name + ' hook did not return a valid value');
1998
+ hook_data = null;
1999
+ } else {
2000
+ if (!_.isArray(ret)) {
2001
+ ret = [ret];
2002
+ }
2003
+ hook_data.splice.apply(hook_data, [0, ret.length].concat(ret));
2004
+ }
2005
+ }, this);
2006
+
2007
+ return hook_data;
1913
2008
  };
1914
2009
 
1915
2010
  /**
@@ -2220,6 +2315,25 @@ MixpanelLib.prototype.report_error = function(msg, err) {
2220
2315
  }
2221
2316
  };
2222
2317
 
2318
+ MixpanelLib.prototype.add_hook = function(hook_name, hook_fn) {
2319
+ if (!this.hooks[hook_name]) {
2320
+ this.hooks[hook_name] = [];
2321
+ }
2322
+ this.hooks[hook_name].push(hook_fn);
2323
+ };
2324
+
2325
+ MixpanelLib.prototype.remove_hook = function(hook_name, hook_fn) {
2326
+ var fn_index;
2327
+ if (this.hooks[hook_name]) {
2328
+ fn_index = this.hooks[hook_name].indexOf(hook_fn);
2329
+ if (fn_index !== -1) {
2330
+ this.hooks[hook_name].splice(fn_index, 1);
2331
+ } else {
2332
+ console.log('remove_hook failed. Matching hook was not found');
2333
+ }
2334
+ }
2335
+ };
2336
+
2223
2337
  // EXPORTS (for closure compiler)
2224
2338
 
2225
2339
  // MixpanelLib Exports
@@ -2252,6 +2366,8 @@ MixpanelLib.prototype['get_group'] = MixpanelLib.protot
2252
2366
  MixpanelLib.prototype['set_group'] = MixpanelLib.prototype.set_group;
2253
2367
  MixpanelLib.prototype['add_group'] = MixpanelLib.prototype.add_group;
2254
2368
  MixpanelLib.prototype['remove_group'] = MixpanelLib.prototype.remove_group;
2369
+ MixpanelLib.prototype['add_hook'] = MixpanelLib.prototype.add_hook;
2370
+ MixpanelLib.prototype['remove_hook'] = MixpanelLib.prototype.remove_hook;
2255
2371
  MixpanelLib.prototype['track_with_groups'] = MixpanelLib.prototype.track_with_groups;
2256
2372
  MixpanelLib.prototype['start_batch_senders'] = MixpanelLib.prototype.start_batch_senders;
2257
2373
  MixpanelLib.prototype['stop_batch_senders'] = MixpanelLib.prototype.stop_batch_senders;
package/src/window.js CHANGED
@@ -15,7 +15,9 @@ if (typeof(window) === 'undefined') {
15
15
  screen: { width: 0, height: 0 },
16
16
  location: loc,
17
17
  addEventListener: function() {},
18
- removeEventListener: function() {}
18
+ removeEventListener: function() {},
19
+ dispatchEvent: function() {},
20
+ CustomEvent: function () {}
19
21
  };
20
22
  } else {
21
23
  win = window;
@@ -1,9 +0,0 @@
1
- {
2
- "permissions": {
3
- "allow": [
4
- "Bash(mkdir:*)"
5
- ],
6
- "deny": [],
7
- "ask": []
8
- }
9
- }