Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
81155 views
1
/**
2
* Copyright 2013-2014, Facebook, Inc.
3
* All rights reserved.
4
*
5
* This source code is licensed under the BSD-style license found in the
6
* LICENSE file in the root directory of this source tree. An additional grant
7
* of patent rights can be found in the PATENTS file in the same directory.
8
*
9
* @providesModule AnalyticsEventPluginFactory
10
*
11
* This module provides a factory method to create the AnalyticsEventPlugin that
12
* can be used to track the usage of React components that are of interest to
13
* the user.
14
*
15
* In order to enable a component for analytics tracking, you need to specify
16
* two additional attributes to the component when you describe the structure of
17
* your component in the render() method:
18
*
19
* 1. 'data-analytics-id': This represents a unique ID that the analytics module
20
* will use to identify this component in all the analytics data. Note that
21
* this is independent of the ref or the DOM id of the element. Over the
22
* lifetime of the product, even if the component id or ref needs to be
23
* changed, as long as you can ensure the analytics ID doesnt change, the
24
* historical data can be correlated. Also note that React does NOT do
25
* anything to guarantee or enforce uniqueness of this ID. If its not unique
26
* the analytics data reported will be incorrect.
27
*
28
* 2. 'data-analytics-events': This is a comma separated list of DOM events that
29
* you want analytics on. React currently supports tracking only on a
30
* distinct set of events (See topLevelTypesToAnalyticsEvent).
31
* If the list contains an event that React does not recognize for analytics
32
* tracking, in __DEV__, an error will be thrown. Note that it is case
33
* sensitive and space sensitive.
34
*
35
* By default the AnalyticsEventPlugin is NOT enabled in React. To use it, you
36
* need to create the plugin using the factory method and add it to the list of
37
* Plugins maintained in the EventPluginHub before your component is rendered.
38
* As creation parameters you can specify two arguments:
39
*
40
* 1. callback: This is a required parameter. In __DEV__, an error will be
41
* thrown if this param is missing. The callback will be called with the
42
* analyticsData as an argument. The analyticsData will contain one property
43
* per every React component, identified by its data-analytics-id. The value
44
* of this property will be an object containing properties corresponding to
45
* each of the comma separated events specified in data-analytics-events.
46
*
47
* For example, if you have:
48
* <Button ...
49
* data-analytics-id="createButton"
50
* data-analytics-events="click"
51
* />
52
* and
53
* <TextBox ...
54
* data-analytics-id="disclaimerBox"
55
* data-analytics-events="focus,scroll"
56
* />
57
* analyticsData will be something like:
58
* '{"createButton":{"click":50}, "disclaimerBox":{"focus":15, "scroll":5}}'
59
*
60
* DO NOT mutate the data that you get in the callback. Mutating it will lead
61
* to errors and unstable behavior.
62
*
63
* The React component will be included for analytics as long as some user
64
* interaction has happened with that component. If no user interaction has
65
* happened with any of the components tracked for analytics, the callback
66
* will not be called.
67
*
68
* 2. interval (in milliseconds): This is an optional parameter to specify the
69
* interval at which the callback needs to be called. It needs to be greater
70
* than the 2 minutes (which is the default value if this parameter is not
71
* specified or a value less than 2 minutes is specified)
72
*
73
* Please refer to the unit tests AnalyticsEventPlugin-test.js for details on
74
* usage.
75
*/
76
77
"use strict";
78
79
var ExecutionEnvironment = require('ExecutionEnvironment');
80
81
var emptyFunction = require('emptyFunction');
82
var invariant = require('invariant');
83
var topLevelTypes = require('EventConstants').topLevelTypes;
84
85
var ANALYTICS_ID = 'data-analytics-id';
86
var ANALYTICS_EVENTS = 'data-analytics-events';
87
var DEFAULT_INTERVAL_MS = 2 * 60 * 1000; // 2 minutes
88
89
var analyticsData = {};
90
91
// List of topLevel event types that React supports for analytics tracking
92
var topLevelTypesToAnalyticsEvent = {
93
topClick: 'click',
94
topDoubleClick: 'doubleClick',
95
wheel: 'wheel',
96
topTouchStart: 'touchStart',
97
topTouchEnd: 'touchEnd',
98
topTouchMove: 'touchMove',
99
topTouchCancel: 'touchCancel',
100
topKeyUp: 'keyUp',
101
topKeyPress: 'keyPress',
102
topKeyDown: 'keyDown',
103
topFocus: 'focus',
104
topBlur: 'blur',
105
topScroll: 'scroll',
106
topChange: 'change'
107
};
108
109
if (__DEV__) {
110
var analyticsEventNameToTopLevelType = {
111
'click': topLevelTypes.topClick,
112
'doubleClick': topLevelTypes.topDoubleClick,
113
'wheel': topLevelTypes.wheel,
114
'touchStart': topLevelTypes.topTouchStart,
115
'touchEnd': topLevelTypes.topTouchEnd,
116
'touchMove': topLevelTypes.topTouchMove,
117
'touchCancel': topLevelTypes.topTouchCancel,
118
'keyUp': topLevelTypes.topKeyUp,
119
'keyPress': topLevelTypes.topKeyPress,
120
'keyDown': topLevelTypes.topKeyDown,
121
'focus': topLevelTypes.topFocus,
122
'blur': topLevelTypes.topBlur,
123
'scroll': topLevelTypes.topScroll,
124
'change': topLevelTypes.topChange
125
};
126
}
127
128
/**
129
* This plugin does not really extract any synthetic events. Rather it just
130
* looks at the top-level event and bumps up counters as appropriate
131
*
132
* @param {string} topLevelType Record from `EventConstants`.
133
* @param {DOMEventTarget} topLevelTarget The listening component root node.
134
* @param {string} topLevelTargetID ID of `topLevelTarget`.
135
* @param {object} nativeEvent Native browser event.
136
* @return {*} An accumulation of synthetic events.
137
* @see {EventPluginHub.extractEvents}
138
*/
139
function extractEvents(
140
topLevelType,
141
topLevelTarget,
142
topLevelTargetID,
143
nativeEvent) {
144
var currentEvent = topLevelTypesToAnalyticsEvent[topLevelType];
145
if (!currentEvent || !topLevelTarget || !topLevelTarget.attributes) {
146
return null;
147
}
148
149
var analyticsID = topLevelTarget.getAttribute(ANALYTICS_ID);
150
var analyticsEventsStr = topLevelTarget.getAttribute(ANALYTICS_EVENTS);
151
if (!analyticsID || !analyticsEventsStr) {
152
return null;
153
}
154
155
var analyticsEventsArr = analyticsEventsStr.split(",");
156
if (!analyticsData.hasOwnProperty(analyticsID)) {
157
initAnalyticsDataForID(analyticsID, analyticsEventsArr);
158
}
159
160
if (analyticsEventsArr.indexOf(currentEvent) !== -1) {
161
analyticsData[analyticsID][currentEvent]++;
162
}
163
164
return null;
165
}
166
167
/**
168
* Initialize the analytics data for a specific element identified by the
169
* analyticsID - Create an entry in the analyticsData object for the element and
170
* initialize all counters for that element to 0.
171
*/
172
function initAnalyticsDataForID(analyticsID, analyticsEventsArr) {
173
analyticsData[analyticsID] = {};
174
analyticsEventsArr.forEach(function(analyticsEvent) {
175
if (__DEV__) {
176
invariant(
177
analyticsEventNameToTopLevelType[analyticsEvent],
178
'Invalid analyticsEvent:%s for analyticsID:%s',
179
analyticsEvent,
180
analyticsID
181
);
182
}
183
analyticsData[analyticsID][analyticsEvent] = 0;
184
});
185
}
186
187
/**
188
* Returns the analytics event plugin given the callback that needs to be
189
* invoked for reporting analytics and the interval at which the callback needs
190
* to be invoked. This interval has to be atleast DEFAULT_INTERVAL_MS.
191
*/
192
var createAnalyticsPlugin = function(cb, interval) {
193
invariant(
194
ExecutionEnvironment.canUseDOM,
195
'createAnalyticsPlugin(...): The DOM is not supported in the execution ' +
196
'environment.'
197
);
198
199
if (__DEV__) {
200
invariant(cb, 'createAnalyticsPlugin(...): You must provide a callback.');
201
}
202
cb = cb || emptyFunction;
203
204
setInterval(
205
function() {
206
if (Object.keys(analyticsData).length) {
207
// Invoke the callback with a clone of analyticsData, otherwise our
208
// analyticsData will be dirtied by user changes
209
cb(analyticsData);
210
}
211
},
212
interval > DEFAULT_INTERVAL_MS ? interval : DEFAULT_INTERVAL_MS
213
);
214
215
return {extractEvents: extractEvents};
216
};
217
218
var AnalyticsEventPluginFactory = {
219
createAnalyticsPlugin: createAnalyticsPlugin
220
};
221
222
module.exports = AnalyticsEventPluginFactory;
223
224