Merge pull request #1563 from coreos-inc/munchkin
Add Marketo munchkin tracking via angulartics
This commit is contained in:
commit
a27c51e81f
10 changed files with 71 additions and 199 deletions
|
@ -1,9 +1,6 @@
|
|||
from uuid import uuid4
|
||||
|
||||
import os.path
|
||||
|
||||
from cryptography.fernet import Fernet
|
||||
|
||||
import requests
|
||||
|
||||
|
||||
|
@ -22,7 +19,7 @@ CLIENT_WHITELIST = ['SERVER_HOSTNAME', 'PREFERRED_URL_SCHEME', 'MIXPANEL_KEY',
|
|||
'STRIPE_PUBLISHABLE_KEY', 'ENTERPRISE_LOGO_URL', 'SENTRY_PUBLIC_DSN',
|
||||
'AUTHENTICATION_TYPE', 'REGISTRY_TITLE', 'REGISTRY_TITLE_SHORT',
|
||||
'CONTACT_INFO', 'AVATAR_KIND', 'LOCAL_OAUTH_HANDLER', 'DOCUMENTATION_LOCATION',
|
||||
'DOCUMENTATION_METADATA', 'SETUP_COMPLETE', 'DEBUG']
|
||||
'DOCUMENTATION_METADATA', 'SETUP_COMPLETE', 'DEBUG', 'MUNCHKIN_KEY']
|
||||
|
||||
|
||||
def frontend_visible_config(config_dict):
|
||||
|
|
|
@ -207,6 +207,7 @@ def render_page_template(name, route_data=None, **kwargs):
|
|||
vuln_priority_set=json.dumps(PRIORITY_LEVELS),
|
||||
enterprise_logo=app.config.get('ENTERPRISE_LOGO_URL', ''),
|
||||
mixpanel_key=app.config.get('MIXPANEL_KEY', ''),
|
||||
munchkin_key=app.config.get('MUNCHKIN_KEY', ''),
|
||||
google_tagmanager_key=app.config.get('GOOGLE_TAGMANAGER_KEY', ''),
|
||||
sentry_public_dsn=app.config.get('SENTRY_PUBLIC_DSN', ''),
|
||||
is_debug=str(app.config.get('DEBUGGING', False)).lower(),
|
||||
|
|
|
@ -39,11 +39,18 @@ quayDependencies = ['ngRoute', 'chieffancypants.loadingBar', 'cfp.hotkeys', 'ang
|
|||
'mgcrea.ngStrap', 'ngCookies', 'ngSanitize', 'angular-md5', 'pasvaz.bindonce', 'ansiToHtml',
|
||||
'core-ui', 'core-config-setup', 'quayPages', 'infinite-scroll'];
|
||||
|
||||
if (window.__config && window.__config.MIXPANEL_KEY) {
|
||||
if (window.__config && (window.__config.MIXPANEL_KEY || window.__config.MUNCHKIN_KEY)) {
|
||||
quayDependencies.push('angulartics');
|
||||
}
|
||||
|
||||
if (window.__config && window.__config.MIXPANEL_KEY) {
|
||||
quayDependencies.push('angulartics.mixpanel');
|
||||
}
|
||||
|
||||
if (window.__config && window.__config.MUNCHKIN_KEY) {
|
||||
quayDependencies.push('angulartics.marketo');
|
||||
}
|
||||
|
||||
// Define the application.
|
||||
quayApp = angular.module('quay', quayDependencies, function($provide, cfpLoadingBarProvider) {
|
||||
cfpLoadingBarProvider.includeSpinner = false;
|
||||
|
|
32
static/lib/angulartics-google-analytics.js
vendored
32
static/lib/angulartics-google-analytics.js
vendored
|
@ -1,32 +0,0 @@
|
|||
/**
|
||||
* @license Angulartics v0.8.5
|
||||
* (c) 2013 Luis Farzati http://luisfarzati.github.io/angulartics
|
||||
* Universal Analytics update contributed by http://github.com/willmcclellan
|
||||
* License: MIT
|
||||
*/
|
||||
(function(angular) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc overview
|
||||
* @name angulartics.google.analytics
|
||||
* Enables analytics support for Google Analytics (http://google.com/analytics)
|
||||
*/
|
||||
angular.module('angulartics.google.analytics', ['angulartics'])
|
||||
.config(['$analyticsProvider', function ($analyticsProvider) {
|
||||
|
||||
// GA already supports buffered invocations so we don't need
|
||||
// to wrap these inside angulartics.waitForVendorApi
|
||||
|
||||
$analyticsProvider.registerPageTrack(function (path) {
|
||||
if (window._gaq) _gaq.push(['_trackPageview', path]);
|
||||
if (window.ga) ga('send', 'pageview', path);
|
||||
});
|
||||
|
||||
$analyticsProvider.registerEventTrack(function (action, properties) {
|
||||
if (window._gaq) _gaq.push(['_trackEvent', properties.category, action, properties.label, properties.value]);
|
||||
if (window.ga) ga('send', 'event', properties.category, action, properties.label, properties.value);
|
||||
});
|
||||
|
||||
}]);
|
||||
})(angular);
|
20
static/lib/angulartics-marketo.min.js
vendored
Normal file
20
static/lib/angulartics-marketo.min.js
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
/**
|
||||
* @license Angulartics
|
||||
* (c) 2014 Carl Thorner http://luisfarzati.github.io/angulartics
|
||||
* Contributed by http://github.com/L42y
|
||||
* License: MIT
|
||||
*/
|
||||
!function(a){"use strict";/**
|
||||
* @ngdoc overview
|
||||
* @name angulartics.marketo
|
||||
* Enables analytics support for Marketo (http://www.marketo.com)
|
||||
*
|
||||
* Will not be considered loaded until the sKey attribute is set on the Munchkin object, like so:
|
||||
*
|
||||
* Munchkin.skey = 'my-secret-key';
|
||||
*
|
||||
* for event tracking email is a required attribute
|
||||
*/
|
||||
a.module("angulartics.marketo",["angulartics"]).config(["$analyticsProvider",function(a){angulartics.waitForVendorApi("Munchkin",500,"sKey",function(b){a.registerPageTrack(function(a){b.munchkinFunction("visitWebPage",{url:a})})}),
|
||||
// If a path is set as a property we do a page tracking event.
|
||||
angulartics.waitForVendorApi("Munchkin",500,"sKey",function(b){a.registerEventTrack(function(a,c){if(void 0!==c.path){var d=[];for(var e in c)"path"!==e&&d.push(e+"="+c[e]);"CLICK"==a.toUpperCase()&&b.munchkinFunction("clickLink",{href:c.path}),b.munchkinFunction("visitWebPage",{url:c.path,params:d.join("&")})}})});var b=function(a){void 0!==a.email&&(email=a.email,email_sha=sha1(Munchkin.sKey+email),a.Email=a.email,Munchkin.munchkinFunction("associateLead",a,email_sha))};angulartics.waitForVendorApi("Munchkin",500,function(c){a.registerSetUsername(function(a){/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}/.test(a)&&b({Email:a})})}),angulartics.waitForVendorApi("Munchkin",500,function(c){a.registerSetUserProperties(function(a){b(a)})}),angulartics.waitForVendorApi("Munchkin",500,function(c){a.registerSetUserPropertiesOnce(function(a){b(a)})})}])}(angular);
|
30
static/lib/angulartics-mixpanel.js
vendored
30
static/lib/angulartics-mixpanel.js
vendored
|
@ -1,30 +0,0 @@
|
|||
/**
|
||||
* @license Angulartics v0.8.5
|
||||
* (c) 2013 Luis Farzati http://luisfarzati.github.io/angulartics
|
||||
* Contributed by http://github.com/L42y
|
||||
* License: MIT
|
||||
*/
|
||||
(function(angular) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc overview
|
||||
* @name angulartics.mixpanel
|
||||
* Enables analytics support for Mixpanel (http://mixpanel.com)
|
||||
*/
|
||||
angular.module('angulartics.mixpanel', ['angulartics'])
|
||||
.config(['$analyticsProvider', function ($analyticsProvider) {
|
||||
angulartics.waitForVendorApi('mixpanel', 500, function (mixpanel) {
|
||||
$analyticsProvider.registerPageTrack(function (path) {
|
||||
if (path.indexOf('http') == 0) { return; }
|
||||
window.mixpanel.track('page_view', { 'url' : path });
|
||||
});
|
||||
});
|
||||
|
||||
angulartics.waitForVendorApi('mixpanel', 500, function (mixpanel) {
|
||||
$analyticsProvider.registerEventTrack(function (action, properties) {
|
||||
window.mixpanel.track(action, properties);
|
||||
});
|
||||
});
|
||||
}]);
|
||||
})(angular);
|
2
static/lib/angulartics-mixpanel.min.js
vendored
Normal file
2
static/lib/angulartics-mixpanel.min.js
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
!function(window,angular,undefined){"use strict";angular.module("angulartics.mixpanel",["angulartics"]).config(["$analyticsProvider",function($analyticsProvider){angulartics.waitForVendorApi("mixpanel",500,"__loaded",function(mixpanel){$analyticsProvider.registerSetUsername(function(userId){mixpanel.identify(userId)}),$analyticsProvider.registerSetAlias(function(userId){mixpanel.alias(userId)}),$analyticsProvider.registerSetSuperPropertiesOnce(function(properties){mixpanel.register_once(properties)}),$analyticsProvider.registerSetSuperProperties(function(properties){mixpanel.register(properties)}),$analyticsProvider.registerSetUserPropertiesOnce(function(properties){mixpanel.people.set_once(properties)}),$analyticsProvider.registerSetUserProperties(function(properties){mixpanel.people.set(properties)}),$analyticsProvider.registerPageTrack(function(path){mixpanel.track("Page Viewed",{page:path})}),$analyticsProvider.registerEventTrack(function(action,properties){mixpanel.track(action,properties)}),$analyticsProvider.registerUserTimings(function(properties,action){mixpanel.time_event(action)})})}])}(window,window.angular);
|
||||
//# sourceMappingURL=../dist/angulartics-mixpanel.min.js.map
|
132
static/lib/angulartics.js
vendored
132
static/lib/angulartics.js
vendored
|
@ -1,132 +0,0 @@
|
|||
/**
|
||||
* @license Angulartics v0.8.5
|
||||
* (c) 2013 Luis Farzati http://luisfarzati.github.io/angulartics
|
||||
* License: MIT
|
||||
*/
|
||||
(function(angular, analytics) {
|
||||
'use strict';
|
||||
|
||||
var angulartics = window.angulartics || (window.angulartics = {});
|
||||
angulartics.waitForVendorApi = function (objectName, delay, registerFn) {
|
||||
if (!window.hasOwnProperty(objectName)) {
|
||||
setTimeout(function () { angulartics.waitForVendorApi(objectName, delay, registerFn); }, delay);
|
||||
}
|
||||
else {
|
||||
registerFn(window[objectName]);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc overview
|
||||
* @name angulartics
|
||||
*/
|
||||
angular.module('angulartics', [])
|
||||
.provider('$analytics', function () {
|
||||
var settings = {
|
||||
pageTracking: {
|
||||
autoTrackFirstPage: true,
|
||||
autoTrackVirtualPages: true,
|
||||
basePath: '',
|
||||
bufferFlushDelay: 1000
|
||||
},
|
||||
eventTracking: {
|
||||
bufferFlushDelay: 1000
|
||||
}
|
||||
};
|
||||
|
||||
var cache = {
|
||||
pageviews: [],
|
||||
events: []
|
||||
};
|
||||
|
||||
var bufferedPageTrack = function (path) {
|
||||
cache.pageviews.push(path);
|
||||
};
|
||||
var bufferedEventTrack = function (event, properties) {
|
||||
cache.events.push({name: event, properties: properties});
|
||||
};
|
||||
|
||||
var api = {
|
||||
settings: settings,
|
||||
pageTrack: bufferedPageTrack,
|
||||
eventTrack: bufferedEventTrack
|
||||
};
|
||||
|
||||
var registerPageTrack = function (fn) {
|
||||
api.pageTrack = fn;
|
||||
angular.forEach(cache.pageviews, function (path, index) {
|
||||
setTimeout(function () { api.pageTrack(path); }, index * settings.pageTracking.bufferFlushDelay);
|
||||
});
|
||||
};
|
||||
var registerEventTrack = function (fn) {
|
||||
api.eventTrack = fn;
|
||||
angular.forEach(cache.events, function (event, index) {
|
||||
setTimeout(function () { api.eventTrack(event.name, event.properties); }, index * settings.eventTracking.bufferFlushDelay);
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
$get: function() { return api; },
|
||||
settings: settings,
|
||||
virtualPageviews: function (value) { this.settings.pageTracking.autoTrackVirtualPages = value; },
|
||||
firstPageview: function (value) { this.settings.pageTracking.autoTrackFirstPage = value; },
|
||||
withBase: function (value) { this.settings.pageTracking.basePath = (value) ? angular.element('base').attr('href').slice(0, -1) : ''; },
|
||||
registerPageTrack: registerPageTrack,
|
||||
registerEventTrack: registerEventTrack
|
||||
};
|
||||
})
|
||||
|
||||
.run(['$rootScope', '$location', '$analytics', function ($rootScope, $location, $analytics) {
|
||||
if ($analytics.settings.pageTracking.autoTrackFirstPage) {
|
||||
$analytics.pageTrack($location.absUrl());
|
||||
}
|
||||
if ($analytics.settings.pageTracking.autoTrackVirtualPages) {
|
||||
$rootScope.$on('$routeChangeSuccess', function (event, current) {
|
||||
if (current && (current.$$route||current).redirectTo) return;
|
||||
var url = $analytics.settings.pageTracking.basePath + $location.url();
|
||||
$analytics.pageTrack(url);
|
||||
});
|
||||
}
|
||||
}])
|
||||
|
||||
.directive('analyticsOn', ['$analytics', function ($analytics) {
|
||||
function isCommand(element) {
|
||||
return ['a:','button:','button:button','button:submit','input:button','input:submit'].indexOf(
|
||||
element.tagName.toLowerCase()+':'+(element.type||'')) >= 0;
|
||||
}
|
||||
|
||||
function inferEventType(element) {
|
||||
if (isCommand(element)) return 'click';
|
||||
return 'click';
|
||||
}
|
||||
|
||||
function inferEventName(element) {
|
||||
if (isCommand(element)) return element.innerText || element.value;
|
||||
return element.id || element.name || element.tagName;
|
||||
}
|
||||
|
||||
function isProperty(name) {
|
||||
return name.substr(0, 9) === 'analytics' && ['on', 'event'].indexOf(name.substr(10)) === -1;
|
||||
}
|
||||
|
||||
return {
|
||||
restrict: 'A',
|
||||
scope: false,
|
||||
link: function ($scope, $element, $attrs) {
|
||||
var eventType = $attrs.analyticsOn || inferEventType($element[0]),
|
||||
eventName = $attrs.analyticsEvent || inferEventName($element[0]);
|
||||
|
||||
var properties = {};
|
||||
angular.forEach($attrs.$attr, function(attr, name) {
|
||||
if (isProperty(attr)) {
|
||||
properties[name.slice(9).toLowerCase()] = $attrs[name];
|
||||
}
|
||||
});
|
||||
|
||||
angular.element($element[0]).bind(eventType, function () {
|
||||
$analytics.eventTrack(eventName, properties);
|
||||
});
|
||||
}
|
||||
};
|
||||
}]);
|
||||
})(angular);
|
32
static/lib/angulartics.min.js
vendored
Normal file
32
static/lib/angulartics.min.js
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
* @license Angulartics
|
||||
* (c) 2013 Luis Farzati http://luisfarzati.github.io/angulartics
|
||||
* License: MIT
|
||||
*/
|
||||
!function(a,b){"use strict";function c(){
|
||||
// General buffering handler
|
||||
function b(a){return function(){k.waitForVendorCount&&(j[a]||(j[a]=[]),j[a].push(arguments))}}
|
||||
// As handlers are installed by plugins, they get pushed into a list and invoked in order.
|
||||
function c(b,c,d){return l[b]||(l[b]=[]),l[b].push(c),m[c]=d,function(){var c=Array.prototype.slice.apply(arguments);return this.$inject(["$q",a.bind(this,function(d){return d.all(l[b].map(function(b){var e=m[b]||{};if(e.async){var f=d.defer(),g=a.copy(c);return g.unshift(f.resolve),b.apply(this,g),f.promise}return d.when(b.apply(this,c))},this))})])}}
|
||||
// Will run setTimeout if delay is > 0
|
||||
// Runs immediately if no delay to make sure cache/buffer is flushed before anything else.
|
||||
// Plugins should take care to register handlers by order of precedence.
|
||||
function d(a,b){b?setTimeout(a,b):a()}
|
||||
// General function to register plugin handlers. Flushes buffers immediately upon registration according to the specified delay.
|
||||
function e(b,e,f){n[b]=c(b,e,f);var g=h[b],i=g?g.bufferFlushDelay:null,k=null!==i?i:h.bufferFlushDelay;a.forEach(j[b],function(a,b){d(function(){e.apply(this,a)},b*k)})}function f(a){return a.replace(/^./,function(a){return a.toUpperCase()})}
|
||||
// Adds to the provider a 'register#{handlerName}' function that manages multiple plugins and buffer flushing.
|
||||
function g(a){var d="register"+f(a);o[d]=function(b,c){e(a,b,c)},n[a]=c(a,b(a))}var h={pageTracking:{autoTrackFirstPage:!0,autoTrackVirtualPages:!0,trackRelativePath:!1,autoBasePath:!1,basePath:"",excludedRoutes:[]},eventTracking:{},bufferFlushDelay:1e3,// Support only one configuration for buffer flush delay to simplify buffering
|
||||
trackExceptions:!1,developerMode:!1},i=["pageTrack","eventTrack","exceptionTrack","setAlias","setUsername","setUserProperties","setUserPropertiesOnce","setSuperProperties","setSuperPropertiesOnce","incrementProperty","userTimings"],j={},l={},m={},n={settings:h},o={$get:["$injector",function(a){return p(a)}],api:n,settings:h,virtualPageviews:function(a){this.settings.pageTracking.autoTrackVirtualPages=a},excludeRoutes:function(a){this.settings.pageTracking.excludedRoutes=a},firstPageview:function(a){this.settings.pageTracking.autoTrackFirstPage=a},withBase:function(b){this.settings.pageTracking.basePath=b?a.element(document).find("base").attr("href"):""},withAutoBase:function(a){this.settings.pageTracking.autoBasePath=a},trackExceptions:function(a){this.settings.trackExceptions=a},developerMode:function(a){this.settings.developerMode=a}},p=function(b){return a.extend(n,{$inject:b.invoke})};
|
||||
// Set up register functions for each known handler
|
||||
a.forEach(i,g);for(var q in o)this[q]=o[q]}function d(b,c,d,e){function f(a){for(var b=0;b<d.settings.pageTracking.excludedRoutes.length;b++){var c=d.settings.pageTracking.excludedRoutes[b];if(c instanceof RegExp&&c.test(a)||a.indexOf(c)>-1)return!0}return!1}function g(a,b){f(a)||d.pageTrack(a,b)}d.settings.pageTracking.autoTrackFirstPage&&e.invoke(["$location",function(a){/* Only track the 'first page' if there are no routes or states on the page */
|
||||
var b=!0;if(e.has("$route")){var f=e.get("$route");if(f)for(var h in f.routes){b=!1;break}else null===f&&(b=!1)}else if(e.has("$state")){var i=e.get("$state");for(var j in i.get()){b=!1;break}}if(b)if(d.settings.pageTracking.autoBasePath&&(d.settings.pageTracking.basePath=c.location.pathname),d.settings.pageTracking.trackRelativePath){var k=d.settings.pageTracking.basePath+a.url();g(k,a)}else g(a.absUrl(),a)}]),d.settings.pageTracking.autoTrackVirtualPages&&e.invoke(["$location",function(a){d.settings.pageTracking.autoBasePath&&(/* Add the full route to the base. */
|
||||
d.settings.pageTracking.basePath=c.location.pathname+"#");var f=!0;if(e.has("$route")){var h=e.get("$route");if(h)for(var i in h.routes){f=!1;break}else null===h&&(f=!1);b.$on("$routeChangeSuccess",function(b,c){if(!c||!(c.$$route||c).redirectTo){var e=d.settings.pageTracking.basePath+a.url();g(e,a)}})}e.has("$state")&&!e.has("$transitions")&&(f=!1,b.$on("$stateChangeSuccess",function(b,c){var e=d.settings.pageTracking.basePath+a.url();g(e,a)})),e.has("$state")&&e.has("$transitions")&&(f=!1,e.invoke(["$transitions",function(b){b.onSuccess({},function(b){var c=b.options();if(c.notify){var e=d.settings.pageTracking.basePath+a.url();g(e,a)}})}])),f&&b.$on("$locationChangeSuccess",function(b,c){if(!c||!(c.$$route||c).redirectTo)if(d.settings.pageTracking.trackRelativePath){var e=d.settings.pageTracking.basePath+a.url();g(e,a)}else g(a.absUrl(),a)})}]),d.settings.developerMode&&a.forEach(d,function(a,b){"function"==typeof a&&(d[b]=function(){})})}function e(b){return{restrict:"A",link:function(c,d,e){var f=e.analyticsOn||"click",g={};a.forEach(e.$attr,function(a,b){i(b)&&(g[j(b)]=e[b],e.$observe(b,function(a){g[j(b)]=a}))}),a.element(d[0]).bind(f,function(f){var i=e.analyticsEvent||h(d[0]);g.eventType=f.type,(!e.analyticsIf||c.$eval(e.analyticsIf))&&(
|
||||
// Allow components to pass through an expression that gets merged on to the event properties
|
||||
// eg. analytics-properites='myComponentScope.someConfigExpression.$analyticsProperties'
|
||||
e.analyticsProperties&&a.extend(g,c.$eval(e.analyticsProperties)),b.eventTrack(i,g))})}}}function f(a){a.decorator("$exceptionHandler",["$delegate","$injector",function(a,b){}])}function g(a){return["a:","button:","button:button","button:submit","input:button","input:submit"].indexOf(a.tagName.toLowerCase()+":"+(a.type||""))>=0}function h(a){return g(a)?a.innerText||a.value:a.id||a.name||a.tagName}function i(a){return"analytics"===a.substr(0,9)&&-1===["On","Event","If","Properties","EventType"].indexOf(a.substr(9))}function j(a){var b=a.slice(9);// slice off the 'analytics' prefix
|
||||
// slice off the 'analytics' prefix
|
||||
return"undefined"!=typeof b&&null!==b&&b.length>0?b.substring(0,1).toLowerCase()+b.substring(1):b}var k=window.angulartics||(window.angulartics={});k.waitForVendorCount=0,k.waitForVendorApi=function(a,b,c,d,e){e||k.waitForVendorCount++,d||(d=c,c=void 0),!Object.prototype.hasOwnProperty.call(window,a)||void 0!==c&&void 0===window[a][c]?setTimeout(function(){k.waitForVendorApi(a,b,c,d,!0)},b):(k.waitForVendorCount--,d(window[a]))},/**
|
||||
* @ngdoc overview
|
||||
* @name angulartics
|
||||
*/
|
||||
a.module("angulartics",[]).provider("$analytics",c).run(["$rootScope","$window","$analytics","$injector",d]).directive("analyticsOn",["$analytics",e]).config(["$provide",f])}(angular);
|
|
@ -64,6 +64,13 @@
|
|||
</script>
|
||||
{% endif %}
|
||||
|
||||
{% if munchkin_key %}
|
||||
<script type="text/javascript">
|
||||
document.write(unescape("%3Cscript src='//munchkin.marketo.net/munchkin.js' type='text/javascript'%3E%3C/script%3E"));
|
||||
</script>
|
||||
<script>Munchkin.init('{{ munchkin_key }}');</script>
|
||||
{% endif %}
|
||||
|
||||
{% if mixpanel_key %}
|
||||
<!-- start Mixpanel --><script type="text/javascript">
|
||||
(function(e,b){if(!b.__SV){var a,f,i,g;window.mixpanel=b;a=e.createElement("script");a.type="text/javascript";a.async=!0;a.src=("https:"===e.location.protocol?"https:":"http:")+'//cdn.mxpnl.com/libs/mixpanel-2.2.min.js';f=e.getElementsByTagName("script")[0];f.parentNode.insertBefore(a,f);b._i=[];b.init=function(a,e,d){function f(b,h){var a=h.split(".");2==a.length&&(b=b[a[0]],h=a[1]);b[h]=function(){b.push([h].concat(Array.prototype.slice.call(arguments,0)))}}var c=b;"undefined"!==
|
||||
|
|
Reference in a new issue