2014-11-28 20:49:41 -05:00
|
|
|
{me} = require 'core/auth'
|
2015-01-14 20:51:04 -05:00
|
|
|
SuperModel = require 'models/SuperModel'
|
2015-03-03 12:04:53 -05:00
|
|
|
utils = require 'core/utils'
|
2014-01-03 13:32:13 -05:00
|
|
|
|
2015-01-31 17:38:54 -05:00
|
|
|
debugAnalytics = false
|
2015-08-03 18:52:52 -04:00
|
|
|
targetInspectJSLevelSlugs = ['cupboards-of-kithgard']
|
2014-04-13 23:31:23 -04:00
|
|
|
|
2014-01-03 13:32:13 -05:00
|
|
|
module.exports = class Tracker
|
|
|
|
constructor: ->
|
|
|
|
if window.tracker
|
2014-06-30 22:16:26 -04:00
|
|
|
console.error 'Overwrote our Tracker!', window.tracker
|
2014-01-03 13:32:13 -05:00
|
|
|
window.tracker = @
|
2014-06-30 22:16:26 -04:00
|
|
|
@isProduction = document.location.href.search('codecombat.com') isnt -1
|
2015-03-03 12:04:53 -05:00
|
|
|
@trackReferrers()
|
2014-01-03 13:32:13 -05:00
|
|
|
@identify()
|
2015-01-14 20:51:04 -05:00
|
|
|
@supermodel = new SuperModel()
|
2014-01-03 13:32:13 -05:00
|
|
|
|
2015-08-03 18:52:52 -04:00
|
|
|
enableInspectletJS: (levelSlug) ->
|
|
|
|
# InspectletJS loading is delayed and targeting specific levels for more focused investigations
|
|
|
|
return @disableInspectletJS() unless levelSlug in targetInspectJSLevelSlugs
|
|
|
|
|
2015-08-14 17:02:19 -04:00
|
|
|
scriptLoaded = =>
|
|
|
|
# Identify and track pageview here, because inspectlet is loaded too late for standard Tracker calls
|
|
|
|
@identify()
|
|
|
|
# http://www.inspectlet.com/docs#virtual_pageviews
|
|
|
|
window.__insp?.push(['virtualPage'])
|
|
|
|
window.__insp = [['wid', 2102699786]]
|
|
|
|
insp = document.createElement('script')
|
|
|
|
insp.type = 'text/javascript'
|
|
|
|
insp.async = true
|
|
|
|
insp.id = 'inspsync'
|
|
|
|
insp.src = (if 'https:' == document.location.protocol then 'https' else 'http') + '://cdn.inspectlet.com/inspectlet.js'
|
|
|
|
insp.onreadystatechange = => scriptLoaded() if insp.readyState is 'complete'
|
|
|
|
insp.onload = scriptLoaded
|
|
|
|
x = document.getElementsByTagName('script')[0]
|
|
|
|
@inspectletScriptNode = x.parentNode.insertBefore insp, x
|
2015-08-03 18:52:52 -04:00
|
|
|
|
|
|
|
disableInspectletJS: ->
|
2015-08-14 17:02:19 -04:00
|
|
|
if @inspectletScriptNode
|
|
|
|
x = document.getElementsByTagName('script')[0]
|
|
|
|
x.parentNode.removeChild(@inspectletScriptNode)
|
|
|
|
@inspectletScriptNode = null
|
2015-08-03 18:52:52 -04:00
|
|
|
delete window.__insp
|
|
|
|
|
2015-03-03 12:04:53 -05:00
|
|
|
trackReferrers: ->
|
|
|
|
elapsed = new Date() - new Date(me.get('dateCreated'))
|
|
|
|
return unless elapsed < 5 * 60 * 1000
|
|
|
|
return if me.get('siteref') or me.get('referrer')
|
2015-03-04 12:00:32 -05:00
|
|
|
changed = false
|
2015-03-03 12:04:53 -05:00
|
|
|
if siteref = utils.getQueryVariable '_r'
|
|
|
|
me.set 'siteref', siteref
|
2015-03-04 12:00:32 -05:00
|
|
|
changed = true
|
2015-03-03 12:04:53 -05:00
|
|
|
if referrer = document.referrer
|
|
|
|
me.set 'referrer', referrer
|
2015-03-04 12:00:32 -05:00
|
|
|
changed = true
|
|
|
|
me.patch() if changed
|
2015-03-03 12:04:53 -05:00
|
|
|
|
2015-02-09 18:02:45 -05:00
|
|
|
identify: (traits={}) ->
|
2015-02-27 19:07:41 -05:00
|
|
|
return unless me
|
|
|
|
|
2015-02-09 18:02:45 -05:00
|
|
|
# Save explicit traits for internal tracking
|
|
|
|
@explicitTraits ?= {}
|
|
|
|
@explicitTraits[key] = value for key, value of traits
|
|
|
|
|
2016-01-22 17:55:12 -05:00
|
|
|
for userTrait in ['email', 'anonymous', 'dateCreated', 'name', 'testGroupNumber', 'gender', 'lastLevel', 'siteref', 'ageRange', 'schoolName', 'coursePrepaidID']
|
2014-01-03 13:32:13 -05:00
|
|
|
traits[userTrait] ?= me.get(userTrait)
|
2016-01-21 13:42:56 -05:00
|
|
|
console.log 'Would identify', me.id, traits if debugAnalytics
|
2015-02-27 19:07:41 -05:00
|
|
|
return unless @isProduction and not me.isAdmin()
|
|
|
|
|
|
|
|
# Errorception
|
|
|
|
# https://errorception.com/docs/meta
|
|
|
|
_errs?.meta = traits
|
|
|
|
|
|
|
|
# Inspectlet
|
|
|
|
# https://www.inspectlet.com/docs#identifying_users
|
|
|
|
__insp?.push ['identify', me.id]
|
|
|
|
__insp?.push ['tagSession', traits]
|
2015-01-09 12:27:29 -05:00
|
|
|
|
2016-01-21 13:42:56 -05:00
|
|
|
# Mixpanel
|
|
|
|
# https://mixpanel.com/help/reference/javascript
|
|
|
|
mixpanel.identify(me.id)
|
|
|
|
mixpanel.register(traits)
|
|
|
|
|
2015-02-27 19:07:41 -05:00
|
|
|
trackPageView: ->
|
|
|
|
name = Backbone.history.getFragment()
|
2016-01-21 13:42:56 -05:00
|
|
|
url = "/#{name}"
|
|
|
|
console.log "Would track analytics pageview: #{url}" if debugAnalytics
|
2015-12-04 19:07:54 -05:00
|
|
|
@trackEventInternal 'Pageview', url: name unless me?.isAdmin() and @isProduction
|
2015-02-27 19:07:41 -05:00
|
|
|
return unless @isProduction and not me.isAdmin()
|
|
|
|
|
|
|
|
# Google Analytics
|
|
|
|
# https://developers.google.com/analytics/devguides/collection/analyticsjs/pages
|
2016-01-21 13:42:56 -05:00
|
|
|
ga? 'send', 'pageview', url
|
|
|
|
|
|
|
|
# Mixpanel
|
|
|
|
mixpanelIncludes = ['courses', 'courses/purchase', 'courses/teachers', 'courses/students', 'schools', 'teachers', 'teachers/freetrial']
|
|
|
|
mixpanel.track('page viewed', 'page name' : name, url : url) if name in mixpanelIncludes
|
2015-02-27 19:07:41 -05:00
|
|
|
|
2016-01-21 13:42:56 -05:00
|
|
|
trackEvent: (action, properties={}, includeIntegrations=[]) =>
|
2015-01-20 17:08:19 -05:00
|
|
|
@trackEventInternal action, _.cloneDeep properties unless me?.isAdmin() and @isProduction
|
2016-01-21 13:42:56 -05:00
|
|
|
console.log 'Tracking external analytics event:', action, properties, includeIntegrations if debugAnalytics
|
2015-02-27 19:07:41 -05:00
|
|
|
return unless me and @isProduction and not me.isAdmin()
|
|
|
|
|
|
|
|
# Google Analytics
|
|
|
|
# https://developers.google.com/analytics/devguides/collection/analyticsjs/events
|
|
|
|
gaFieldObject =
|
|
|
|
hitType: 'event'
|
|
|
|
eventCategory: properties.category ? 'All'
|
|
|
|
eventAction: action
|
|
|
|
gaFieldObject.eventLabel = properties.label if properties.label?
|
|
|
|
gaFieldObject.eventValue = properties.value if properties.value?
|
|
|
|
ga? 'send', gaFieldObject
|
2015-01-09 12:27:29 -05:00
|
|
|
|
2015-02-27 19:07:41 -05:00
|
|
|
# Inspectlet
|
|
|
|
# http://www.inspectlet.com/docs#tagging
|
|
|
|
__insp?.push ['tagSession', action: action, properies: properties]
|
2014-12-18 00:53:59 -05:00
|
|
|
|
2016-01-21 13:42:56 -05:00
|
|
|
# Mixpanel
|
|
|
|
# Only log explicit events for now
|
|
|
|
mixpanel.track(action, properties) if 'Mixpanel' in includeIntegrations
|
|
|
|
|
2015-01-14 20:51:04 -05:00
|
|
|
trackEventInternal: (event, properties) =>
|
|
|
|
# Skipping heavily logged actions we don't use internally
|
|
|
|
unless event in ['Simulator Result', 'Started Level Load', 'Finished Level Load']
|
|
|
|
# Trimming properties we don't use internally
|
2015-02-09 18:02:45 -05:00
|
|
|
# TODO: delete properites.level for 'Saw Victory' after 2/8/15. Should be using levelID instead.
|
2015-02-10 17:20:14 -05:00
|
|
|
if event in ['Clicked Start Level', 'Inventory Play', 'Heard Sprite', 'Started Level', 'Saw Victory', 'Click Play', 'Choose Inventory', 'Homepage Loaded', 'Change Hero']
|
2015-01-28 20:58:56 -05:00
|
|
|
delete properties.category
|
2015-01-14 20:51:04 -05:00
|
|
|
delete properties.label
|
2015-02-10 17:20:14 -05:00
|
|
|
else if event in ['Loaded World Map', 'Started Signup', 'Finished Signup', 'Login', 'Facebook Login', 'Google Login', 'Show subscription modal']
|
2015-01-28 20:58:56 -05:00
|
|
|
delete properties.category
|
2015-01-15 14:04:48 -05:00
|
|
|
|
2015-02-09 18:02:45 -05:00
|
|
|
properties[key] = value for key, value of @explicitTraits if @explicitTraits?
|
2015-01-14 20:51:04 -05:00
|
|
|
console.log 'Tracking internal analytics event:', event, properties if debugAnalytics
|
2015-02-27 14:44:06 -05:00
|
|
|
if @isProduction
|
|
|
|
eventObject = {}
|
|
|
|
eventObject["event"] = event
|
|
|
|
eventObject["properties"] = properties unless _.isEmpty properties
|
|
|
|
eventObject["user"] = me.id
|
|
|
|
dataToSend = JSON.stringify eventObject
|
2015-02-27 19:07:41 -05:00
|
|
|
# console.log dataToSend if debugAnalytics
|
2015-04-18 19:08:31 -04:00
|
|
|
$.post("#{window.location.protocol or 'http:'}//analytics.codecombat.com/analytics", dataToSend).fail ->
|
2015-02-27 14:44:06 -05:00
|
|
|
console.error "Analytics post failed!"
|
|
|
|
else
|
2016-01-25 19:52:14 -05:00
|
|
|
request = @supermodel.addRequestResource {
|
2015-11-25 19:33:27 -05:00
|
|
|
url: '/db/analytics.log.event/-/log_event'
|
2015-02-27 14:44:06 -05:00
|
|
|
data: {event: event, properties: properties}
|
|
|
|
method: 'POST'
|
|
|
|
}, 0
|
|
|
|
request.load()
|
2014-04-13 23:31:23 -04:00
|
|
|
|
2015-02-27 19:07:41 -05:00
|
|
|
trackTiming: (duration, category, variable, label) ->
|
|
|
|
# https://developers.google.com/analytics/devguides/collection/analyticsjs/user-timings
|
2014-04-13 23:31:23 -04:00
|
|
|
return console.warn "Duration #{duration} invalid for trackTiming call." unless duration >= 0 and duration < 60 * 60 * 1000
|
2014-06-30 22:16:26 -04:00
|
|
|
console.log 'Would track timing event:', arguments if debugAnalytics
|
2015-02-27 19:07:41 -05:00
|
|
|
return unless me and @isProduction and not me.isAdmin()
|
|
|
|
ga? 'send', 'timing', category, variable, duration, label
|