react / react-0.13.3 / examples / basic-commonjs / node_modules / reactify / node_modules / react-tools / src / browser / eventPlugins / AnalyticsEventPluginFactory.js
81155 views/**1* Copyright 2013-2014, Facebook, Inc.2* All rights reserved.3*4* This source code is licensed under the BSD-style license found in the5* LICENSE file in the root directory of this source tree. An additional grant6* of patent rights can be found in the PATENTS file in the same directory.7*8* @providesModule AnalyticsEventPluginFactory9*10* This module provides a factory method to create the AnalyticsEventPlugin that11* can be used to track the usage of React components that are of interest to12* the user.13*14* In order to enable a component for analytics tracking, you need to specify15* two additional attributes to the component when you describe the structure of16* your component in the render() method:17*18* 1. 'data-analytics-id': This represents a unique ID that the analytics module19* will use to identify this component in all the analytics data. Note that20* this is independent of the ref or the DOM id of the element. Over the21* lifetime of the product, even if the component id or ref needs to be22* changed, as long as you can ensure the analytics ID doesnt change, the23* historical data can be correlated. Also note that React does NOT do24* anything to guarantee or enforce uniqueness of this ID. If its not unique25* the analytics data reported will be incorrect.26*27* 2. 'data-analytics-events': This is a comma separated list of DOM events that28* you want analytics on. React currently supports tracking only on a29* distinct set of events (See topLevelTypesToAnalyticsEvent).30* If the list contains an event that React does not recognize for analytics31* tracking, in __DEV__, an error will be thrown. Note that it is case32* sensitive and space sensitive.33*34* By default the AnalyticsEventPlugin is NOT enabled in React. To use it, you35* need to create the plugin using the factory method and add it to the list of36* Plugins maintained in the EventPluginHub before your component is rendered.37* As creation parameters you can specify two arguments:38*39* 1. callback: This is a required parameter. In __DEV__, an error will be40* thrown if this param is missing. The callback will be called with the41* analyticsData as an argument. The analyticsData will contain one property42* per every React component, identified by its data-analytics-id. The value43* of this property will be an object containing properties corresponding to44* each of the comma separated events specified in data-analytics-events.45*46* For example, if you have:47* <Button ...48* data-analytics-id="createButton"49* data-analytics-events="click"50* />51* and52* <TextBox ...53* data-analytics-id="disclaimerBox"54* data-analytics-events="focus,scroll"55* />56* analyticsData will be something like:57* '{"createButton":{"click":50}, "disclaimerBox":{"focus":15, "scroll":5}}'58*59* DO NOT mutate the data that you get in the callback. Mutating it will lead60* to errors and unstable behavior.61*62* The React component will be included for analytics as long as some user63* interaction has happened with that component. If no user interaction has64* happened with any of the components tracked for analytics, the callback65* will not be called.66*67* 2. interval (in milliseconds): This is an optional parameter to specify the68* interval at which the callback needs to be called. It needs to be greater69* than the 2 minutes (which is the default value if this parameter is not70* specified or a value less than 2 minutes is specified)71*72* Please refer to the unit tests AnalyticsEventPlugin-test.js for details on73* usage.74*/7576"use strict";7778var ExecutionEnvironment = require('ExecutionEnvironment');7980var emptyFunction = require('emptyFunction');81var invariant = require('invariant');82var topLevelTypes = require('EventConstants').topLevelTypes;8384var ANALYTICS_ID = 'data-analytics-id';85var ANALYTICS_EVENTS = 'data-analytics-events';86var DEFAULT_INTERVAL_MS = 2 * 60 * 1000; // 2 minutes8788var analyticsData = {};8990// List of topLevel event types that React supports for analytics tracking91var topLevelTypesToAnalyticsEvent = {92topClick: 'click',93topDoubleClick: 'doubleClick',94wheel: 'wheel',95topTouchStart: 'touchStart',96topTouchEnd: 'touchEnd',97topTouchMove: 'touchMove',98topTouchCancel: 'touchCancel',99topKeyUp: 'keyUp',100topKeyPress: 'keyPress',101topKeyDown: 'keyDown',102topFocus: 'focus',103topBlur: 'blur',104topScroll: 'scroll',105topChange: 'change'106};107108if (__DEV__) {109var analyticsEventNameToTopLevelType = {110'click': topLevelTypes.topClick,111'doubleClick': topLevelTypes.topDoubleClick,112'wheel': topLevelTypes.wheel,113'touchStart': topLevelTypes.topTouchStart,114'touchEnd': topLevelTypes.topTouchEnd,115'touchMove': topLevelTypes.topTouchMove,116'touchCancel': topLevelTypes.topTouchCancel,117'keyUp': topLevelTypes.topKeyUp,118'keyPress': topLevelTypes.topKeyPress,119'keyDown': topLevelTypes.topKeyDown,120'focus': topLevelTypes.topFocus,121'blur': topLevelTypes.topBlur,122'scroll': topLevelTypes.topScroll,123'change': topLevelTypes.topChange124};125}126127/**128* This plugin does not really extract any synthetic events. Rather it just129* looks at the top-level event and bumps up counters as appropriate130*131* @param {string} topLevelType Record from `EventConstants`.132* @param {DOMEventTarget} topLevelTarget The listening component root node.133* @param {string} topLevelTargetID ID of `topLevelTarget`.134* @param {object} nativeEvent Native browser event.135* @return {*} An accumulation of synthetic events.136* @see {EventPluginHub.extractEvents}137*/138function extractEvents(139topLevelType,140topLevelTarget,141topLevelTargetID,142nativeEvent) {143var currentEvent = topLevelTypesToAnalyticsEvent[topLevelType];144if (!currentEvent || !topLevelTarget || !topLevelTarget.attributes) {145return null;146}147148var analyticsID = topLevelTarget.getAttribute(ANALYTICS_ID);149var analyticsEventsStr = topLevelTarget.getAttribute(ANALYTICS_EVENTS);150if (!analyticsID || !analyticsEventsStr) {151return null;152}153154var analyticsEventsArr = analyticsEventsStr.split(",");155if (!analyticsData.hasOwnProperty(analyticsID)) {156initAnalyticsDataForID(analyticsID, analyticsEventsArr);157}158159if (analyticsEventsArr.indexOf(currentEvent) !== -1) {160analyticsData[analyticsID][currentEvent]++;161}162163return null;164}165166/**167* Initialize the analytics data for a specific element identified by the168* analyticsID - Create an entry in the analyticsData object for the element and169* initialize all counters for that element to 0.170*/171function initAnalyticsDataForID(analyticsID, analyticsEventsArr) {172analyticsData[analyticsID] = {};173analyticsEventsArr.forEach(function(analyticsEvent) {174if (__DEV__) {175invariant(176analyticsEventNameToTopLevelType[analyticsEvent],177'Invalid analyticsEvent:%s for analyticsID:%s',178analyticsEvent,179analyticsID180);181}182analyticsData[analyticsID][analyticsEvent] = 0;183});184}185186/**187* Returns the analytics event plugin given the callback that needs to be188* invoked for reporting analytics and the interval at which the callback needs189* to be invoked. This interval has to be atleast DEFAULT_INTERVAL_MS.190*/191var createAnalyticsPlugin = function(cb, interval) {192invariant(193ExecutionEnvironment.canUseDOM,194'createAnalyticsPlugin(...): The DOM is not supported in the execution ' +195'environment.'196);197198if (__DEV__) {199invariant(cb, 'createAnalyticsPlugin(...): You must provide a callback.');200}201cb = cb || emptyFunction;202203setInterval(204function() {205if (Object.keys(analyticsData).length) {206// Invoke the callback with a clone of analyticsData, otherwise our207// analyticsData will be dirtied by user changes208cb(analyticsData);209}210},211interval > DEFAULT_INTERVAL_MS ? interval : DEFAULT_INTERVAL_MS212);213214return {extractEvents: extractEvents};215};216217var AnalyticsEventPluginFactory = {218createAnalyticsPlugin: createAnalyticsPlugin219};220221module.exports = AnalyticsEventPluginFactory;222223224