mixpanel-browser
Version:
The official Mixpanel JavaScript browser client library
159 lines (126 loc) • 4.02 kB
JavaScript
/* eslint camelcase: "off" */
import { _, console } from './utils';
/**
* DomTracker Object
* @constructor
*/
var DomTracker = function() {};
// interface
DomTracker.prototype.create_properties = function() {};
DomTracker.prototype.event_handler = function() {};
DomTracker.prototype.after_track_handler = function() {};
DomTracker.prototype.init = function(mixpanel_instance) {
this.mp = mixpanel_instance;
return this;
};
/**
* @param {Object|string} query
* @param {string} event_name
* @param {Object=} properties
* @param {function=} user_callback
*/
DomTracker.prototype.track = function(query, event_name, properties, user_callback) {
var that = this;
var elements = _.dom_query(query);
if (elements.length === 0) {
console.error('The DOM query (' + query + ') returned 0 elements');
return;
}
_.each(elements, function(element) {
_.register_event(element, this.override_event, function(e) {
var options = {};
var props = that.create_properties(properties, this);
var timeout = that.mp.get_config('track_links_timeout');
that.event_handler(e, this, options);
// in case the mixpanel servers don't get back to us in time
window.setTimeout(that.track_callback(user_callback, props, options, true), timeout);
// fire the tracking event
that.mp.track(event_name, props, that.track_callback(user_callback, props, options));
});
}, this);
return true;
};
/**
* @param {function} user_callback
* @param {Object} props
* @param {boolean=} timeout_occured
*/
DomTracker.prototype.track_callback = function(user_callback, props, options, timeout_occured) {
timeout_occured = timeout_occured || false;
var that = this;
return function() {
// options is referenced from both callbacks, so we can have
// a 'lock' of sorts to ensure only one fires
if (options.callback_fired) { return; }
options.callback_fired = true;
if (user_callback && user_callback(timeout_occured, props) === false) {
// user can prevent the default functionality by
// returning false from their callback
return;
}
that.after_track_handler(props, options, timeout_occured);
};
};
DomTracker.prototype.create_properties = function(properties, element) {
var props;
if (typeof(properties) === 'function') {
props = properties(element);
} else {
props = _.extend({}, properties);
}
return props;
};
/**
* LinkTracker Object
* @constructor
* @extends DomTracker
*/
var LinkTracker = function() {
this.override_event = 'click';
};
_.inherit(LinkTracker, DomTracker);
LinkTracker.prototype.create_properties = function(properties, element) {
var props = LinkTracker.superclass.create_properties.apply(this, arguments);
if (element.href) { props['url'] = element.href; }
return props;
};
LinkTracker.prototype.event_handler = function(evt, element, options) {
options.new_tab = (
evt.which === 2 ||
evt.metaKey ||
evt.ctrlKey ||
element.target === '_blank'
);
options.href = element.href;
if (!options.new_tab) {
evt.preventDefault();
}
};
LinkTracker.prototype.after_track_handler = function(props, options) {
if (options.new_tab) { return; }
setTimeout(function() {
window.location = options.href;
}, 0);
};
/**
* FormTracker Object
* @constructor
* @extends DomTracker
*/
var FormTracker = function() {
this.override_event = 'submit';
};
_.inherit(FormTracker, DomTracker);
FormTracker.prototype.event_handler = function(evt, element, options) {
options.element = element;
evt.preventDefault();
};
FormTracker.prototype.after_track_handler = function(props, options) {
setTimeout(function() {
options.element.submit();
}, 0);
};
export {
FormTracker,
LinkTracker
};