├── src ├── providers │ ├── crazyegg │ │ ├── crazyegg-test.js │ │ └── crazyegg.js │ ├── intercom │ │ ├── intercom-test.js │ │ └── intercom.js │ ├── chartbeat │ │ ├── chartbeat-test.js │ │ └── chartbeat.js │ ├── ga │ │ ├── ga-test.js │ │ └── ga.js │ ├── klaviyo │ │ ├── klaviyo.js │ │ └── klaviyo-test.js │ ├── kissmetrics │ │ ├── kissmetrics.js │ │ └── kissmetrics-test.js │ ├── customerio │ │ ├── customerio-test.js │ │ └── customerio.js │ ├── hubspot │ │ ├── hubspot.js │ │ └── hubspot-test.js │ ├── gosquared │ │ ├── gosquared.js │ │ └── gosquared-test.js │ ├── olark │ │ ├── olark-test.js │ │ └── olark.js │ └── mixpanel │ │ ├── mixpanel.js │ │ └── mixpanel-test.js ├── analytics-test.js └── analytics.js ├── .travis.yml ├── .gitignore ├── test ├── cloud.js ├── server.js ├── libs │ ├── mocha │ │ ├── mocha-cloud-0.0.1.js │ │ └── mocha-1.7.4.css │ └── sinon-chai │ │ └── sinon-chai-2.2.0.js ├── core.html ├── min.html └── providers.html ├── README.md ├── package.json ├── History.md ├── Makefile ├── docs ├── crazyegg.html ├── docco.css ├── chartbeat.html ├── klaviyo.html ├── kissmetrics.html ├── gosquared.html ├── hubspot.html ├── ga.html ├── intercom.html └── customerio.html └── analytics.min.js /src/providers/crazyegg/crazyegg-test.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 0.8 4 | 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.sublime* 3 | node_modules/ 4 | test/pid.txt 5 | -------------------------------------------------------------------------------- /test/cloud.js: -------------------------------------------------------------------------------- 1 | 2 | var Cloud = require('mocha-cloud') 3 | , cloud = new Cloud('Analytics.js', process.env.SAUCE_USERNAME, 4 | process.env.SAUCE_ACCESS_KEY); 5 | 6 | cloud.browser('Firefox', '3.6', 'Linux'); 7 | cloud.url('http://localhost:8000/test/providers.html'); 8 | 9 | cloud.on('init', function (browser) { 10 | console.log(browser); 11 | }); 12 | 13 | cloud.on('end', function (browser, res) { 14 | console.log(browser, res); 15 | }); 16 | 17 | cloud.start(); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | analytics.js 2 | ============ 3 | 4 | [![Build Status](https://travis-ci.org/segmentio/analytics.js.png)](https://travis-ci.org/segmentio/analytics.js) 5 | 6 | The hassle-free way to integrate analytics into any web application. [See the docs.](http://segmentio.github.com/analytics.js/) 7 | 8 | Looking for an _even_ easier way to setup analytics on your website? [Checkout out segment.io!](https://segment.io) It's a hosted solution that lets you add analytics services without touching any code at all. 9 | -------------------------------------------------------------------------------- /test/server.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Simple connect server for phantom.js 3 | * Adapted from twitter bootstrap server. 4 | */ 5 | 6 | var connect = require('connect') 7 | , http = require('http') 8 | , fs = require('fs') 9 | , path = require('path') 10 | , app = connect(); 11 | 12 | var pidFile = path.resolve(__dirname, './pid.txt'); 13 | 14 | app.use(connect.static(path.resolve(__dirname, '../'))); 15 | 16 | http.createServer(app).listen(8000, function () { 17 | fs.writeFileSync(pidFile, process.pid, 'utf-8'); 18 | }); 19 | 20 | -------------------------------------------------------------------------------- /src/providers/intercom/intercom-test.js: -------------------------------------------------------------------------------- 1 | /*global sinon, suite, beforeEach, test, expect, analytics */ 2 | !(function () { 3 | 4 | suite('Intercom'); 5 | 6 | test('stores settings on initialize', function () { 7 | analytics.initialize({ 8 | 'Intercom' : 'x' 9 | }); 10 | expect(analytics.providers[0].settings.appId).to.equal('x'); 11 | }); 12 | 13 | test('adds intercom javascript on identify', function () { 14 | expect(window.intercomSettings).not.to.exist; 15 | 16 | analytics.identify('zeus@segment.io', { name : 'Zeus' }); 17 | expect(window.intercomSettings).to.exist; 18 | }); 19 | 20 | }()); -------------------------------------------------------------------------------- /test/libs/mocha/mocha-cloud-0.0.1.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Mocha cloud runner, adapted from visionmedia/mocha-cloud/client.js 3 | */ 4 | (function (global) { 5 | 6 | var mochaCloud = function (runner) { 7 | var failed = []; 8 | 9 | runner.on('fail', function(test, err){ 10 | failed.push({ 11 | title: test.title, 12 | fullTitle: test.fullTitle(), 13 | error: { 14 | message: err.message, 15 | stack: err.stack 16 | } 17 | }); 18 | }); 19 | 20 | runner.on('end', function(){ 21 | runner.stats.failed = failed; 22 | global.mochaResults = runner.stats; 23 | }); 24 | }; 25 | 26 | global.mochaCloud = mochaCloud; 27 | 28 | })(window); 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "analytics", 3 | "author" : "Segmentio ", 4 | "version" : "0.2.1", 5 | "description" : "The hassle-free way to integrate analytics into any web application.", 6 | "keywords" : [ 7 | "analytics", "analytics.js", "analyticsjs", "segment", "segmentio", "segment.io", 8 | "chartbeat", "crazyegg", "customer.io", "google analytics", "hubspot", 9 | "gosquared", "intercom", "kissmetrics", "klaviyo", "mixpanel", "olark" 10 | ], 11 | "url" : "https://github.com/segmentio/analytics.js", 12 | "repository" : { 13 | "type" : "git", 14 | "url" : "git://github.com/segmentio/analytics.js.git" 15 | }, 16 | "main" : "analytics.js", 17 | "license" : "MIT", 18 | "scripts" : { "test" : "make test" }, 19 | "devDependencies": { 20 | "mocha-cloud" : "0.0.1", 21 | "mocha-phantomjs": "1.1.1", 22 | "connect" : "2.7.1" 23 | } 24 | } -------------------------------------------------------------------------------- /src/providers/crazyegg/crazyegg.js: -------------------------------------------------------------------------------- 1 | // CrazyEgg 2 | // -------- 3 | // [Documentation](www.crazyegg.com). 4 | 5 | analytics.addProvider('CrazyEgg', { 6 | 7 | settings : { 8 | apiKey : null 9 | }, 10 | 11 | 12 | // Initialize 13 | // ---------- 14 | 15 | // Changes to the CrazyEgg snippet: 16 | // 17 | // * Concatenate `apiKey` into the URL. 18 | initialize : function (settings) { 19 | settings = analytics.utils.resolveSettings(settings, 'apiKey'); 20 | analytics.utils.extend(this.settings, settings); 21 | 22 | (function(){ 23 | var a=document.createElement("script"); 24 | var b=document.getElementsByTagName("script")[0]; 25 | a.src=document.location.protocol+"//dnn506yrbagrg.cloudfront.net/pages/scripts/"+this.settings.apiKey+".js?"+Math.floor(new Date().getTime()/3600000); 26 | a.async=true;a.type="text/javascript";b.parentNode.insertBefore(a,b); 27 | })(); 28 | } 29 | 30 | }); 31 | 32 | 33 | -------------------------------------------------------------------------------- /test/core.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Core Tests | Analytics.js 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |

Core Test

27 |
28 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/providers/chartbeat/chartbeat-test.js: -------------------------------------------------------------------------------- 1 | /*global sinon, suite, beforeEach, test, expect, analytics */ 2 | !(function () { 3 | 4 | suite('Chartbeat'); 5 | 6 | 7 | // Initialize 8 | // ---------- 9 | 10 | test('stores settings and adds chartbeat.js on initialize', function (done) { 11 | expect(window.pSUPERFLY).not.to.exist; 12 | 13 | analytics.initialize({ 14 | 'Chartbeat' : { 15 | uid : 'x', 16 | domain : 'example.com' 17 | } 18 | }); 19 | // We have to wait for the charbeat.js to come back and create the 20 | // global variable on window... 21 | var self = this; 22 | setTimeout(function () { 23 | expect(window.pSUPERFLY).to.exist; 24 | expect(analytics.providers[0].settings.uid).to.equal('x'); 25 | expect(analytics.providers[0].settings.domain).to.equal('example.com'); 26 | done(); 27 | }, 500); 28 | }); 29 | 30 | 31 | // Pageview 32 | // -------- 33 | 34 | test('calls virtualPage on pageview', function () { 35 | var spy = sinon.spy(window.pSUPERFLY, 'virtualPage'); 36 | analytics.pageview(); 37 | expect(spy).to.have.been.calledWith(window.location.pathname); 38 | 39 | spy.restore(); 40 | }); 41 | 42 | }()); -------------------------------------------------------------------------------- /src/providers/ga/ga-test.js: -------------------------------------------------------------------------------- 1 | /*global sinon, suite, beforeEach, test, expect, analytics */ 2 | !(function () { 3 | 4 | suite('Google Analytics'); 5 | 6 | 7 | // Initialize 8 | // ---------- 9 | 10 | test('stores settings and adds ga.js on initialize', function () { 11 | expect(window._gaq).not.to.exist; 12 | 13 | analytics.initialize({ 14 | 'Google Analytics' : 'x' 15 | }); 16 | expect(window._gaq).to.exist; 17 | expect(analytics.providers[0].settings.trackingId).to.equal('x'); 18 | }); 19 | 20 | test('can add enhanced link attribution'); 21 | 22 | test('can add site speed sample rate'); 23 | 24 | test('can add anonymize ip'); 25 | 26 | 27 | // Track 28 | // ----- 29 | 30 | test('pushes "_trackEvent" on track', function () { 31 | var spy = sinon.spy(window._gaq, 'push'); 32 | analytics.track('event'); 33 | expect(spy).to.have.been.calledWith(['_trackEvent', 'All', 'event']); 34 | 35 | spy.restore(); 36 | }); 37 | 38 | 39 | // Pageview 40 | // -------- 41 | 42 | test('pushes "_trackPageview" on pageview', function () { 43 | var spy = sinon.spy(window._gaq, 'push'); 44 | analytics.pageview(); 45 | expect(spy).to.have.been.calledWith(['_trackPageview']); 46 | 47 | spy.restore(); 48 | }); 49 | 50 | }()); -------------------------------------------------------------------------------- /History.md: -------------------------------------------------------------------------------- 1 | 0.2.1 / 2012-12-18 2 | ================== 3 | 4 | * Added the `pageview` method for tracking virtual pageviews 5 | * Added Travis-CI 6 | * Fixed window level objects in customerio and gosquared 7 | 8 | 9 | 0.2.0 / 2012-12-16 10 | ================== 11 | 12 | * Separated providers into separate files for easier maintenance 13 | * Changed special `createdAt` trait to `created` for cleanliness 14 | * Moved `utils` directly onto the analytics object 15 | * Added `extend` and `alias` utils 16 | * Added `settings` defaults for all providers 17 | 18 | 19 | 0.1.2 / 2012-12-14 20 | ================== 21 | 22 | * Fixed bug with HubSpot calls pre-script load 23 | * Upgraded sinon-chai to use [callWithMatch version](https://github.com/obmarg/sinon-chai/blob/f7aa7eccd6c0c18a3e1fc524a246a50c1a29c916/lib/sinon-chai.js) 24 | * Klaviyo support added by [@bialecki](https://github.com/bialecki) 25 | * HubSpot support added by [@jessbrandi](https://github.com/jessbrandi) 26 | * GoSquared support added by [@simontabor](https://github.com/simontabor) 27 | 28 | 29 | 0.1.1 / 2012-10-25 30 | ================== 31 | 32 | * Enhanced Link Attribution added for Google Analytics by [@nscott](https://github.com/nscott) 33 | * Site Speed Sample Rate added for Google Analytics by [@nscott](https://github.com/nscott) 34 | 35 | 36 | 0.1.0 / 2012-10-11 37 | ================== 38 | 39 | * Added Olark provider 40 | * Added terse `initialize` syntax 41 | * Added tests for all providers 42 | * Added README 43 | 44 | -------------------------------------------------------------------------------- /src/providers/klaviyo/klaviyo.js: -------------------------------------------------------------------------------- 1 | // Klaviyo 2 | // ------- 3 | // [Documentation](https://www.klaviyo.com/docs). 4 | // [Documentation](https://www.klaviyo.com/docs/http-api). 5 | 6 | analytics.addProvider('Klaviyo', { 7 | 8 | settings : { 9 | apiKey : null 10 | }, 11 | 12 | 13 | // Initialize 14 | // ---------- 15 | 16 | // Changes to the Google Analytics snippet: 17 | // 18 | // * Added `apiKey`. 19 | initialize : function (settings) { 20 | settings = analytics.utils.resolveSettings(settings, 'apiKey'); 21 | analytics.utils.extend(this.settings, settings); 22 | 23 | var _learnq = _learnq || []; 24 | _learnq.push(['account', this.settings.apiKey]); 25 | (function () { 26 | var b = document.createElement('script'); b.type = 'text/javascript'; b.async = true; 27 | b.src = ('https:' == document.location.protocol ? 'https://' : 'http://') + 28 | 'a.klaviyo.com/media/js/learnmarklet.js'; 29 | var a = document.getElementsByTagName('script')[0]; a.parentNode.insertBefore(b, a); 30 | })(); 31 | 32 | window._learnq = _learnq; 33 | }, 34 | 35 | 36 | // Identify 37 | // -------- 38 | 39 | identify : function (userId, traits) { 40 | // Klaviyo takes the user ID on the traits object itself. 41 | traits || (traits = {}); 42 | if (userId) traits.$id = userId; 43 | 44 | window._learnq.push(['identify', traits]); 45 | }, 46 | 47 | 48 | // Track 49 | // ----- 50 | 51 | track : function (event, properties) { 52 | window._learnq.push(['track', event, properties]); 53 | } 54 | 55 | }); 56 | 57 | 58 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | analytics.js: 2 | cat \ 3 | src/analytics.js \ 4 | src/providers/chartbeat/chartbeat.js \ 5 | src/providers/crazyegg/crazyegg.js \ 6 | src/providers/customerio/customerio.js \ 7 | src/providers/ga/ga.js \ 8 | src/providers/hubspot/hubspot.js \ 9 | src/providers/gosquared/gosquared.js \ 10 | src/providers/intercom/intercom.js \ 11 | src/providers/kissmetrics/kissmetrics.js \ 12 | src/providers/klaviyo/klaviyo.js \ 13 | src/providers/mixpanel/mixpanel.js \ 14 | src/providers/olark/olark.js \ 15 | > analytics.js 16 | 17 | min: analytics.js 18 | uglifyjs -o analytics.min.js analytics.js 19 | 20 | docs: 21 | docco \ 22 | src/analytics.js \ 23 | src/providers/chartbeat/chartbeat.js \ 24 | src/providers/crazyegg/crazyegg.js \ 25 | src/providers/customerio/customerio.js \ 26 | src/providers/ga/ga.js \ 27 | src/providers/hubspot/hubspot.js \ 28 | src/providers/gosquared/gosquared.js \ 29 | src/providers/intercom/intercom.js \ 30 | src/providers/kissmetrics/kissmetrics.js \ 31 | src/providers/klaviyo/klaviyo.js \ 32 | src/providers/mixpanel/mixpanel.js \ 33 | src/providers/olark/olark.js 34 | 35 | server: 36 | node test/server.js & 37 | 38 | # Kills the travis server 39 | kill: 40 | kill -9 `cat test/pid.txt` 41 | rm test/pid.txt 42 | 43 | # Runs travis tests 44 | test: server 45 | sleep 1 46 | node_modules/.bin/mocha-phantomjs http://localhost:8000/test/min.html 47 | node_modules/.bin/mocha-phantomjs http://localhost:8000/test/providers.html 48 | node_modules/.bin/mocha-phantomjs http://localhost:8000/test/core.html 49 | make kill 50 | 51 | release: 52 | make analytics.js 53 | make min 54 | make docs 55 | make test 56 | 57 | .PHONY: analytics.js docs test 58 | -------------------------------------------------------------------------------- /src/providers/kissmetrics/kissmetrics.js: -------------------------------------------------------------------------------- 1 | // KISSmetrics 2 | // ----------- 3 | // [Documentation](http://support.kissmetrics.com/apis/javascript). 4 | 5 | analytics.addProvider('KISSmetrics', { 6 | 7 | settings : { 8 | apiKey : null 9 | }, 10 | 11 | 12 | // Initialize 13 | // ---------- 14 | 15 | // Changes to the KISSmetrics snippet: 16 | // 17 | // * Concatenate the `apiKey` into the URL. 18 | initialize : function (settings) { 19 | settings = analytics.utils.resolveSettings(settings, 'apiKey'); 20 | analytics.utils.extend(this.settings, settings); 21 | 22 | var _kmq = _kmq || []; 23 | function _kms(u){ 24 | setTimeout(function(){ 25 | var d = document, f = d.getElementsByTagName('script')[0], 26 | s = d.createElement('script'); 27 | s.type = 'text/javascript'; s.async = true; s.src = u; 28 | f.parentNode.insertBefore(s, f); 29 | }, 1); 30 | } 31 | _kms('//i.kissmetrics.com/i.js'); 32 | _kms('//doug1izaerwt3.cloudfront.net/'+this.settings.apiKey+'.1.js'); 33 | 34 | window._kmq = _kmq; 35 | }, 36 | 37 | 38 | // Identify 39 | // -------- 40 | 41 | // KISSmetrics uses two separate methods: `identify` for storing the 42 | // `userId`, and `set` for storing `traits`. 43 | identify : function (userId, traits) { 44 | if (userId) window._kmq.push(['identify', userId]); 45 | if (traits) window._kmq.push(['set', traits]); 46 | }, 47 | 48 | 49 | // Track 50 | // ----- 51 | 52 | track : function (event, properties) { 53 | window._kmq.push(['record', event, properties]); 54 | } 55 | 56 | }); 57 | 58 | 59 | -------------------------------------------------------------------------------- /src/providers/customerio/customerio-test.js: -------------------------------------------------------------------------------- 1 | /*global sinon, suite, beforeEach, test, expect, analytics */ 2 | !(function () { 3 | 4 | suite('Customer.io'); 5 | 6 | var event = 'event'; 7 | 8 | var properties = { 9 | count : 42 10 | }; 11 | 12 | var userId = 'user'; 13 | 14 | var traits = { 15 | name : 'Zeus', 16 | created : new Date('12/30/1989') 17 | }; 18 | 19 | 20 | // Initialize 21 | // ---------- 22 | 23 | test('adds customer.io\'s track.js on initialize', function () { 24 | expect(window._cio).not.to.exist; 25 | 26 | analytics.initialize({ 27 | 'Customer.io' : 'x' 28 | }); 29 | expect(window._cio).to.exist; 30 | expect(analytics.providers[0].settings.siteId).to.equal('x'); 31 | }); 32 | 33 | 34 | // Identify 35 | // -------- 36 | 37 | test('calls identify on identify', function () { 38 | var spy = sinon.spy(window._cio, 'identify'); 39 | analytics.identify(traits); 40 | expect(spy).to.not.have.been.called; 41 | 42 | spy.reset(); 43 | analytics.identify(userId, traits); 44 | expect(spy).to.have.been.calledWith({ 45 | id : userId, 46 | name : traits.name, 47 | created_at : Math.floor((new Date('12/30/1989')).getTime() / 1000) 48 | }); 49 | 50 | spy.restore(); 51 | }); 52 | 53 | 54 | // Track 55 | // ----- 56 | 57 | test('calls track on track', function () { 58 | var spy = sinon.spy(window._cio, 'track'); 59 | analytics.track(event, properties); 60 | expect(spy).to.have.been.calledWith(event, properties); 61 | 62 | spy.restore(); 63 | }); 64 | 65 | }()); -------------------------------------------------------------------------------- /src/providers/chartbeat/chartbeat.js: -------------------------------------------------------------------------------- 1 | // Chartbeat 2 | // --------- 3 | // [Documentation](http://chartbeat.com/docs/adding_the_code/), 4 | // [documentation](http://chartbeat.com/docs/configuration_variables/), 5 | // [documentation](http://chartbeat.com/docs/handling_virtual_page_changes/). 6 | 7 | analytics.addProvider('Chartbeat', { 8 | 9 | settings : { 10 | domain : null, 11 | uid : null 12 | }, 13 | 14 | 15 | // Initialize 16 | // ---------- 17 | 18 | // Changes to the Chartbeat snippet: 19 | // 20 | // * Pass `settings` directly as the config object. 21 | // * Replaced the date with our stored `date` variable. 22 | initialize : function (settings) { 23 | settings = analytics.utils.resolveSettings(settings, 'uid'); 24 | analytics.utils.extend(this.settings, settings); 25 | 26 | // Since all the custom settings just get passed through, update the 27 | // Chartbeat `_sf_async_config` variable with settings. 28 | var _sf_async_config = this.settings || {}; 29 | 30 | (function(){ 31 | // Use the stored date from when we were loaded. 32 | window._sf_endpt = analytics.date.getTime(); 33 | var e = document.createElement("script"); 34 | e.setAttribute("language", "javascript"); 35 | e.setAttribute("type", "text/javascript"); 36 | e.setAttribute("src", 37 | (("https:" == document.location.protocol) ? 38 | "https://a248.e.akamai.net/chartbeat.download.akamai.com/102508/" : 39 | "http://static.chartbeat.com/") + 40 | "js/chartbeat.js"); 41 | document.body.appendChild(e); 42 | })(); 43 | }, 44 | 45 | 46 | // Pageview 47 | // -------- 48 | 49 | pageview : function () { 50 | window.pSUPERFLY.virtualPage(window.location.pathname); 51 | } 52 | 53 | }); 54 | 55 | 56 | -------------------------------------------------------------------------------- /src/providers/hubspot/hubspot.js: -------------------------------------------------------------------------------- 1 | // HubSpot 2 | // ------- 3 | // [Documentation](http://hubspot.clarify-it.com/d/4m62hl) 4 | 5 | analytics.addProvider('HubSpot', { 6 | 7 | settings : { 8 | portalId : null 9 | }, 10 | 11 | 12 | // Initialize 13 | // ---------- 14 | 15 | // Changes to the HubSpot snippet: 16 | // 17 | // * Concatenate `portalId` into the URL. 18 | initialize : function (settings) { 19 | settings = analytics.utils.resolveSettings(settings, 'portalId'); 20 | analytics.utils.extend(this.settings, settings); 21 | 22 | var self = this; 23 | 24 | (function(d,s,i,r) { 25 | if (d.getElementById(i)){return;} 26 | window._hsq = window._hsq || []; // for calls pre-load 27 | var n=d.createElement(s),e=d.getElementsByTagName(s)[0]; 28 | n.id=i;n.src='https://js.hubspot.com/analytics/'+(Math.ceil(new Date()/r)*r)+'/' + self.settings.portalId + '.js'; 29 | e.parentNode.insertBefore(n, e); 30 | })(document,"script","hs-analytics",300000); 31 | }, 32 | 33 | 34 | // Identify 35 | // -------- 36 | 37 | identify : function (userId, traits) { 38 | // HubSpot does not use a userId, but the email address is required on 39 | // the traits object. 40 | if (!traits) return; 41 | 42 | window._hsq.push(["identify", traits]); 43 | }, 44 | 45 | 46 | // Track 47 | // ----- 48 | 49 | // Event Tracking is available to HubSpot Enterprise customers only. In 50 | // addition to adding any unique event name, you can also use the id of an 51 | // existing custom event as the event variable. 52 | track : function (event, properties) { 53 | window._hsq.push(["trackEvent", event, properties]); 54 | }, 55 | 56 | 57 | // Pageview 58 | // -------- 59 | 60 | pageview : function () { 61 | // TODO http://performabledoc.hubspot.com/display/DOC/JavaScript+API 62 | } 63 | 64 | }); 65 | 66 | 67 | -------------------------------------------------------------------------------- /src/providers/klaviyo/klaviyo-test.js: -------------------------------------------------------------------------------- 1 | /*global _, sinon, suite, beforeEach, test, expect, analytics */ 2 | !(function () { 3 | 4 | suite('Klaviyo'); 5 | 6 | var event = 'event'; 7 | 8 | var properties = { 9 | count : 42 10 | }; 11 | 12 | var userId = 'user'; 13 | 14 | var traits = { 15 | name : 'Zeus', 16 | email : 'zeus@segment.io' 17 | }; 18 | 19 | 20 | // Initialize 21 | // ---------- 22 | 23 | test('stores settings and adds kissmetrics javascript on initialize', function () { 24 | expect(window._learnq).not.to.exist; 25 | 26 | analytics.initialize({ 27 | 'Klaviyo' : 'x' 28 | }); 29 | expect(window._learnq).to.exist; 30 | expect(analytics.providers[0].settings.apiKey).to.equal('x'); 31 | }); 32 | 33 | 34 | // Identify 35 | // -------- 36 | 37 | test('pushes "_identify" on identify', function () { 38 | var spy = sinon.spy(window._learnq, 'push'); 39 | analytics.identify(traits); 40 | expect(spy).to.have.been.calledWith(['identify', traits]); 41 | 42 | spy.reset(); 43 | analytics.identify(userId); 44 | expect(spy).to.have.been.calledWith(['identify', { $id: userId }]); 45 | 46 | spy.reset(); 47 | var augmentedTraits = _.extend({}, traits, { $id: userId }); 48 | analytics.identify(userId, traits); 49 | expect(spy).to.have.been.calledWith(['identify', augmentedTraits]); 50 | 51 | spy.restore(); 52 | }); 53 | 54 | 55 | // Track 56 | // ----- 57 | 58 | test('pushes "_track" on track', function () { 59 | var spy = sinon.spy(window._learnq, 'push'); 60 | analytics.track(event, properties); 61 | // Klaviyo adds extra properites to the event, so we don't want to check 62 | // for an exact match. 63 | expect(spy).to.have.been.calledWithMatch(['track', event, sinon.match(properties)]); 64 | 65 | spy.restore(); 66 | }); 67 | 68 | }()); -------------------------------------------------------------------------------- /test/min.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Minified Tests | Analytics.js 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 |

Minified Test

37 |
38 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/providers/gosquared/gosquared.js: -------------------------------------------------------------------------------- 1 | // GoSquared 2 | // --------- 3 | // [Documentation](www.gosquared.com/support). 4 | // Will automatically [integrate with Olark](https://www.gosquared.com/support/articles/721791-setting-up-olark-live-chat). 5 | 6 | analytics.addProvider('GoSquared', { 7 | 8 | settings : { 9 | siteToken : null 10 | }, 11 | 12 | 13 | // Initialize 14 | // ---------- 15 | 16 | // Changes to the GoSquared tracking code: 17 | // 18 | // * Use `siteToken` from settings. 19 | // * No longer need to wait for pageload, removed unnecessary functions. 20 | // * Attach `GoSquared` to `window`. 21 | 22 | initialize : function (settings) { 23 | settings = analytics.utils.resolveSettings(settings, 'siteToken'); 24 | analytics.utils.extend(this.settings, settings); 25 | 26 | var GoSquared = window.GoSquared = {}; 27 | GoSquared.acct = this.settings.siteToken; 28 | window._gstc_lt=+(new Date); var d=document; 29 | var g = d.createElement("script"); g.type = "text/javascript"; g.async = true; g.src = "//d1l6p2sc9645hc.cloudfront.net/tracker.js"; 30 | var s = d.getElementsByTagName("script")[0]; s.parentNode.insertBefore(g, s); 31 | }, 32 | 33 | 34 | // Identify 35 | // -------- 36 | 37 | identify : function (userId, traits) { 38 | // TODO figure out if this will actually work. Seems like GoSquared will 39 | // never know these values are updated. 40 | if (userId) window.GoSquared.UserName = userId; 41 | if (traits) window.GoSquared.Visitor = traits; 42 | }, 43 | 44 | 45 | // Track 46 | // ----- 47 | 48 | track : function (event, properties) { 49 | // The queue isn't automatically created by the snippet. 50 | if (!window.GoSquared.q) window.GoSquared.q = []; 51 | window.GoSquared.q.push(['TrackEvent', event, properties]); 52 | }, 53 | 54 | 55 | // Pageview 56 | // -------- 57 | 58 | pageview : function () { 59 | window.GoSquared.DefaultTracker.TrackView(); 60 | }, 61 | 62 | }); 63 | 64 | 65 | -------------------------------------------------------------------------------- /src/providers/hubspot/hubspot-test.js: -------------------------------------------------------------------------------- 1 | /*global sinon, suite, beforeEach, test, expect, analytics */ 2 | !(function () { 3 | 4 | suite('HubSpot'); 5 | 6 | var event = 'event'; 7 | 8 | var properties = { 9 | count : 42 10 | }; 11 | 12 | var userId = 'user'; 13 | 14 | var traits = { 15 | name : 'Zeus', 16 | email : 'zeus@segment.io' 17 | }; 18 | 19 | 20 | // Initialize 21 | // ---------- 22 | 23 | test('stores settings and adds hubspot js on initialize', function (done) { 24 | expect(window._hsq).not.to.exist; 25 | 26 | analytics.initialize({ 27 | 'HubSpot' : 'x' 28 | }); 29 | expect(window._hsq).to.exist; 30 | expect(window._hsq.push).to.equal(Array.prototype.push); 31 | expect(analytics.providers[0].settings.portalId).to.equal('x'); 32 | 33 | // Once the hubspot JS file comes back, the array should be transformed. 34 | var self = this; 35 | setTimeout(function () { 36 | expect(window._hsq).to.exist; 37 | expect(window._hsq).to.not.equal(Array.prototype.push); 38 | done(); 39 | }, 100); 40 | }); 41 | 42 | 43 | // Identify 44 | // -------- 45 | 46 | test('pushes "identify" on identify', function () { 47 | var spy = sinon.spy(window._hsq, 'push'); 48 | analytics.identify(traits); 49 | expect(spy).to.have.been.calledWith(['identify', traits]); 50 | 51 | spy.reset(); 52 | analytics.identify(userId); 53 | expect(spy).to.not.have.been.calledWith(['identify', userId]); 54 | 55 | spy.reset(); 56 | analytics.identify(userId, traits); 57 | expect(spy).to.have.been.calledWith(['identify', traits]); 58 | 59 | spy.restore(); 60 | }); 61 | 62 | 63 | // Track 64 | // ----- 65 | 66 | test('pushes "trackEvent" on track', function () { 67 | var spy = sinon.spy(window._hsq, 'push'); 68 | analytics.track(event, properties); 69 | expect(spy).to.have.been.calledWith(['trackEvent', event, properties]); 70 | }); 71 | 72 | }()); -------------------------------------------------------------------------------- /src/providers/kissmetrics/kissmetrics-test.js: -------------------------------------------------------------------------------- 1 | /*global sinon, suite, beforeEach, test, expect, analytics */ 2 | !(function () { 3 | 4 | suite('KISSmetrics'); 5 | 6 | var event = 'event'; 7 | 8 | var properties = { 9 | count : 42 10 | }; 11 | 12 | var userId = 'user'; 13 | 14 | var traits = { 15 | name : 'Zeus', 16 | email : 'zeus@segment.io' 17 | }; 18 | 19 | 20 | // Initialize 21 | // ---------- 22 | 23 | test('stores settings and adds kissmetrics javascript on initialize', function () { 24 | expect(window._kmq).not.to.exist; 25 | 26 | analytics.initialize({ 27 | 'KISSmetrics' : 'x' 28 | }); 29 | expect(window._kmq).to.exist; 30 | expect(analytics.providers[0].settings.apiKey).to.equal('x'); 31 | }); 32 | 33 | 34 | // Identify 35 | // -------- 36 | 37 | test('pushes "_identify" on identify', function () { 38 | var spy = sinon.spy(window._kmq, 'push'); 39 | analytics.identify(traits); 40 | expect(spy).to.have.not.been.calledWith(['identify', userId]); 41 | 42 | spy.reset(); 43 | analytics.identify(userId); 44 | expect(spy).to.have.been.calledWith(['identify', userId]); 45 | 46 | spy.reset(); 47 | analytics.identify(userId, traits); 48 | expect(spy).to.have.been.calledWith(['identify', userId]); 49 | 50 | spy.restore(); 51 | }); 52 | 53 | test('pushes "_set" on identify', function () { 54 | var spy = sinon.spy(window._kmq, 'push'); 55 | analytics.identify(traits); 56 | expect(spy).to.have.been.calledWith(['set', traits]); 57 | 58 | spy.reset(); 59 | analytics.identify(userId); 60 | expect(spy).to.have.not.been.calledWith(['set', traits]); 61 | 62 | spy.reset(); 63 | analytics.identify(userId, traits); 64 | expect(spy).to.have.been.calledWith(['set', traits]); 65 | 66 | spy.restore(); 67 | }); 68 | 69 | 70 | // Track 71 | // ----- 72 | 73 | test('pushes "_record" on track', function () { 74 | var spy = sinon.spy(window._kmq, 'push'); 75 | analytics.track(event, properties); 76 | expect(spy).to.have.been.calledWith(['record', event, properties]); 77 | 78 | spy.restore(); 79 | }); 80 | 81 | }()); -------------------------------------------------------------------------------- /src/providers/intercom/intercom.js: -------------------------------------------------------------------------------- 1 | // Intercom 2 | // -------- 3 | // [Documentation](http://docs.intercom.io/). 4 | 5 | analytics.addProvider('Intercom', { 6 | 7 | settings : { 8 | appId : null 9 | }, 10 | 11 | 12 | // Initialize 13 | // ---------- 14 | 15 | // Intercom identifies when the script is loaded, so instead of initializing 16 | // in `initialize`, we store the settings for later and initialize in 17 | // `identify`. 18 | initialize: function (settings) { 19 | settings = analytics.utils.resolveSettings(settings, 'appId'); 20 | analytics.utils.extend(this.settings, settings); 21 | }, 22 | 23 | 24 | // Identify 25 | // -------- 26 | 27 | // Changes to the Intercom snippet: 28 | // 29 | // * Add `appId` from stored `settings`. 30 | // * Add `userId`. 31 | identify: function (userId, traits) { 32 | // Don't do anything if we just have traits. 33 | if (!userId) return; 34 | 35 | // Pass traits directly in to Intercom's `custom_data`. 36 | window.intercomSettings = { 37 | app_id : this.settings.appId, 38 | user_id : userId, 39 | custom_data : traits || {}, 40 | }; 41 | 42 | // Augment `intercomSettings` with some of the special traits. 43 | if (traits) { 44 | window.intercomSettings.email = traits.email; 45 | window.intercomSettings.name = traits.name; 46 | window.intercomSettings.created_at = analytics.utils.getSeconds(traits.created); 47 | } 48 | 49 | // If they didn't pass an email, check to see if the `userId` qualifies. 50 | if (analytics.utils.isEmail(userId) && (traits && !traits.email)) { 51 | window.intercomSettings.email = userId; 52 | } 53 | 54 | function async_load() { 55 | var s = document.createElement('script'); 56 | s.type = 'text/javascript'; s.async = true; 57 | s.src = 'https://api.intercom.io/api/js/library.js'; 58 | var x = document.getElementsByTagName('script')[0]; 59 | x.parentNode.insertBefore(s, x); 60 | } 61 | if (window.attachEvent) { 62 | window.attachEvent('onload', async_load); 63 | } else { 64 | window.addEventListener('load', async_load, false); 65 | } 66 | } 67 | 68 | }); 69 | 70 | 71 | -------------------------------------------------------------------------------- /src/providers/ga/ga.js: -------------------------------------------------------------------------------- 1 | // Google Analytics 2 | // ---------------- 3 | // [Documentation](https://developers.google.com/analytics/devguides/collection/gajs/). 4 | 5 | analytics.addProvider('Google Analytics', { 6 | 7 | settings : { 8 | anonymizeIp : false, 9 | enhancedLinkAttribution : false, 10 | siteSpeedSampleRate : null, 11 | trackingId : null 12 | }, 13 | 14 | 15 | // Initialize 16 | // ---------- 17 | 18 | // Changes to the Google Analytics snippet: 19 | // 20 | // * Added `trackingId`. 21 | // * Added optional support for `enhancedLinkAttribution` 22 | // * Added optional support for `siteSpeedSampleRate` 23 | // * Added optional support for `anonymizeIp` 24 | initialize : function (settings) { 25 | settings = analytics.utils.resolveSettings(settings, 'trackingId'); 26 | analytics.utils.extend(this.settings, settings); 27 | 28 | var _gaq = _gaq || []; 29 | _gaq.push(['_setAccount', this.settings.trackingId]); 30 | if (this.settings.enhancedLinkAttribution) { 31 | var pluginUrl = (('https:' == document.location.protocol) ? 'https://www.' : 'http://www.') + 'google-analytics.com/plugins/ga/inpage_linkid.js'; 32 | _gaq.push(['_require', 'inpage_linkid', pluginUrl]); 33 | } 34 | if (analytics.utils.isNumber(this.settings.siteSpeedSampleRate)) { 35 | _gaq.push(['_setSiteSpeedSampleRate', this.settings.siteSpeedSampleRate]); 36 | } 37 | if(this.settings.anonymizeIp) { 38 | _gaq.push(['_gat._anonymizeIp']); 39 | } 40 | _gaq.push(['_trackPageview']); 41 | (function() { 42 | var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; 43 | ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; 44 | var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); 45 | })(); 46 | 47 | window._gaq = _gaq; 48 | }, 49 | 50 | 51 | // Track 52 | // ----- 53 | 54 | track : function (event, properties) { 55 | window._gaq.push(['_trackEvent', 'All', event]); 56 | }, 57 | 58 | 59 | // Pageview 60 | // -------- 61 | 62 | pageview : function () { 63 | window._gaq.push(['_trackPageview']); 64 | } 65 | 66 | }); 67 | 68 | 69 | -------------------------------------------------------------------------------- /src/providers/customerio/customerio.js: -------------------------------------------------------------------------------- 1 | // Customer.io 2 | // ----------- 3 | // [Documentation](http://customer.io/docs/api/javascript.html). 4 | 5 | analytics.addProvider('Customer.io', { 6 | 7 | settings : { 8 | siteId : null 9 | }, 10 | 11 | 12 | // Initialize 13 | // ---------- 14 | 15 | // Changes to the Chartbeat snippet: 16 | // 17 | // * Add `siteId`. 18 | initialize : function (settings) { 19 | settings = analytics.utils.resolveSettings(settings, 'siteId'); 20 | analytics.utils.extend(this.settings, settings); 21 | 22 | var self = this; 23 | 24 | var _cio = window._cio = _cio || []; 25 | (function() { 26 | var a,b,c;a=function(f){return function(){_cio.push([f]. 27 | concat(Array.prototype.slice.call(arguments,0)))}};b=["identify", 28 | "track"];for(c=0;c 2 | 3 | 4 | 5 | 6 | Providers Tests | Analytics.js 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 |

Providers Test

46 |
47 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /src/providers/gosquared/gosquared-test.js: -------------------------------------------------------------------------------- 1 | /*global sinon, suite, beforeEach, test, expect, analytics */ 2 | !(function () { 3 | 4 | suite('GoSquared'); 5 | 6 | var event = 'event'; 7 | 8 | var properties = { 9 | count : 42 10 | }; 11 | 12 | var userId = 'user'; 13 | 14 | var traits = { 15 | name : 'Zeus', 16 | email : 'zeus@segment.io' 17 | }; 18 | 19 | 20 | // Initialize 21 | // ---------- 22 | 23 | test('stores settings and adds GoSquared js on initialize', function () { 24 | expect(window.GoSquared).not.to.exist; 25 | 26 | analytics.initialize({ 27 | 'GoSquared' : 'x' 28 | }); 29 | expect(window.GoSquared).to.exist; 30 | expect(analytics.providers[0].settings.siteToken).to.equal('x'); 31 | }); 32 | 33 | test('GoSquared tracker finishes loading', function (done) { 34 | // use the GoSquared.load function... 35 | window.GoSquared.load = function(tracker) { 36 | expect(window.GoSquared.DefaultTracker).to.equal(tracker); 37 | done(); 38 | }; 39 | }); 40 | 41 | 42 | // Identify 43 | // -------- 44 | 45 | test('correctly identifies the user', function () { 46 | expect(window.GoSquared.UserName).not.to.exist; 47 | expect(window.GoSquared.Visitor).not.to.exist; 48 | 49 | analytics.identify(traits); 50 | expect(window.GoSquared.UserName).not.to.exist; 51 | expect(window.GoSquared.Visitor).to.deep.equal(traits); 52 | 53 | window.GoSquared.Visitor = undefined; 54 | analytics.identify(userId); 55 | expect(window.GoSquared.UserName).to.equal(userId); 56 | expect(window.GoSquared.Visitor).not.to.exist; 57 | 58 | window.GoSquared.UserName = undefined; 59 | analytics.identify(userId, traits); 60 | expect(window.GoSquared.UserName).to.equal(userId); 61 | expect(window.GoSquared.Visitor).to.deep.equal(traits); 62 | }); 63 | 64 | 65 | // Track 66 | // ----- 67 | 68 | test('pushes "TrackEvent" on track', function () { 69 | var spy = sinon.spy(window.GoSquared.q, 'push'); 70 | analytics.track(event, properties); 71 | var augmentedProperties = _.extend(properties, { gs_evt_name: event }); 72 | expect(spy).to.have.been.calledWith([event, sinon.match(augmentedProperties)]); 73 | 74 | spy.restore(); 75 | }); 76 | 77 | 78 | // Pageview 79 | // -------- 80 | 81 | test('calls "TrackView" on pageview', function () { 82 | var spy = sinon.spy(window.GoSquared.DefaultTracker, 'TrackView'); 83 | analytics.pageview(); 84 | expect(spy).to.have.been.called; 85 | 86 | spy.restore(); 87 | }); 88 | 89 | }()); -------------------------------------------------------------------------------- /src/providers/olark/olark-test.js: -------------------------------------------------------------------------------- 1 | /*global sinon, suite, beforeEach, test, expect, analytics */ 2 | !(function () { 3 | 4 | suite('Olark'); 5 | 6 | var event = 'event'; 7 | 8 | var properties = { 9 | count : 42 10 | }; 11 | 12 | var userId = 'user'; 13 | 14 | var traits = { 15 | name : 'Zeus', 16 | email : 'zeus@segment.io' 17 | }; 18 | 19 | 20 | // Initialize 21 | // ---------- 22 | 23 | test('stores settings and adds olark.js on initialize', function () { 24 | expect(window.olark).not.to.exist; 25 | 26 | analytics.initialize({ 27 | 'Olark' : 'x' 28 | }); 29 | expect(window.olark).to.exist; 30 | expect(analytics.providers[0].settings.siteId).to.equal('x'); 31 | }); 32 | 33 | 34 | // Identify 35 | // -------- 36 | 37 | test('updates visitor nickname on identify with the best name', function () { 38 | var spy = sinon.spy(window, 'olark'); 39 | analytics.identify({ 40 | dogs : 1 41 | }); 42 | expect(spy).not.to.have.been.called; 43 | 44 | spy.reset(); 45 | analytics.identify({ 46 | email : 'zeus@segment.io' 47 | }); 48 | expect(spy).to.have.been.calledWith('api.chat.updateVisitorNickname', sinon.match({ 49 | snippet : 'zeus@segment.io' 50 | })); 51 | 52 | spy.reset(); 53 | analytics.identify(traits); 54 | expect(spy).to.have.been.calledWith('api.chat.updateVisitorNickname', sinon.match({ 55 | snippet : 'Zeus (zeus@segment.io)' 56 | })); 57 | 58 | spy.reset(); 59 | analytics.identify(userId); 60 | expect(spy).to.have.been.calledWith('api.chat.updateVisitorNickname', sinon.match({ 61 | snippet : 'user' 62 | })); 63 | 64 | spy.reset(); 65 | analytics.identify(userId, traits); 66 | expect(spy).to.have.been.calledWith('api.chat.updateVisitorNickname', sinon.match({ 67 | snippet : 'Zeus (zeus@segment.io)' 68 | })); 69 | 70 | spy.restore(); 71 | }); 72 | 73 | 74 | // Track 75 | // ----- 76 | 77 | test('logs event to operator on track if `track` setting is true', function () { 78 | analytics.providers[0].settings.track = true; 79 | var spy = sinon.spy(window, 'olark'); 80 | analytics.track(event, properties); 81 | expect(spy).to.have.been.calledWith('api.chat.sendNotificationToOperator', sinon.match({ 82 | body : 'visitor triggered "'+event+'"' 83 | })); 84 | 85 | spy.restore(); 86 | }); 87 | 88 | test('doesnt log event to operator on track if `track` setting is false', function () { 89 | analytics.providers[0].settings.track = false; 90 | var spy = sinon.spy(window, 'olark'); 91 | analytics.track(event, properties); 92 | expect(spy).not.to.have.been.called; 93 | 94 | spy.restore(); 95 | }); 96 | 97 | 98 | // Pageview 99 | // -------- 100 | 101 | test('logs event to operator on pageview if `pageview` setting is true', function () { 102 | analytics.providers[0].settings.pageview = true; 103 | var spy = sinon.spy(window, 'olark'); 104 | analytics.pageview(); 105 | expect(spy).to.have.been.calledWith('api.chat.sendNotificationToOperator', sinon.match({ 106 | body : 'looking at ' + window.location.href 107 | })); 108 | 109 | spy.restore(); 110 | }); 111 | 112 | test('doesnt log event to operator on pageview if `pageview` setting is false', function () { 113 | analytics.providers[0].settings.pageview = false; 114 | var spy = sinon.spy(window, 'olark'); 115 | analytics.pageview(); 116 | expect(spy).not.to.have.been.called; 117 | 118 | spy.restore(); 119 | }); 120 | 121 | }()); -------------------------------------------------------------------------------- /src/providers/mixpanel/mixpanel.js: -------------------------------------------------------------------------------- 1 | // Mixpanel 2 | // -------- 3 | // [Documentation](https://mixpanel.com/docs/integration-libraries/javascript), 4 | // [documentation](https://mixpanel.com/docs/people-analytics/javascript), 5 | // [documentation](https://mixpanel.com/docs/integration-libraries/javascript-full-api). 6 | 7 | analytics.addProvider('Mixpanel', { 8 | 9 | settings : { 10 | nameTag : true, 11 | people : false, 12 | token : null 13 | }, 14 | 15 | 16 | // Initialize 17 | // ---------- 18 | 19 | // Changes to the Mixpanel snippet: 20 | // 21 | // * Use window for call to `init`. 22 | // * Add `token` and `settings` args to call to `init`. 23 | // 24 | // We don't need to set the `mixpanel` object on `window` ourselves because 25 | // they already do that. 26 | initialize : function (settings) { 27 | settings = analytics.utils.resolveSettings(settings, 'token'); 28 | analytics.utils.extend(this.settings, settings); 29 | 30 | (function(c,a){window.mixpanel=a;var b,d,h,e;b=c.createElement("script"); 31 | b.type="text/javascript";b.async=!0;b.src=("https:"===c.location.protocol?"https:":"http:")+ 32 | '//cdn.mxpnl.com/libs/mixpanel-2.1.min.js';d=c.getElementsByTagName("script")[0]; 33 | d.parentNode.insertBefore(b,d);a._i=[];a.init=function(b,c,f){function d(a,b){ 34 | var c=b.split(".");2==c.length&&(a=a[c[0]],b=c[1]);a[b]=function(){a.push([b].concat( 35 | Array.prototype.slice.call(arguments,0)))}}var g=a;"undefined"!==typeof f?g=a[f]=[]: 36 | f="mixpanel";g.people=g.people||[];h=['disable','track','track_pageview','track_links', 37 | 'track_forms','register','register_once','unregister','identify','name_tag', 38 | 'set_config','people.identify','people.set','people.increment'];for(e=0;e<",i,' onl' + 'oad="var d=',g,";d.getElementsByTagName('head')[0].",j,"(d.",h,"('script')).",k,"='",l,"//",a.l,"'",'"',">"].join("")}var i="body",m=d[i];if(!m){return setTimeout(ld,100)}a.P(1);var j="appendChild",h="createElement",k="src",n=d[h]("div"),v=n[j](d[h](z)),b=d[h]("iframe"),g="document",e="domain",o;n.style.display="none";m.insertBefore(n,m.firstChild).id=z;b.frameBorder="0";b.id=z+"-loader";if(/MSIE[ ]+6/.test(navigator.userAgent)){b.src="javascript:false"}b.allowTransparency="true";v[j](b);try{b.contentWindow[g].open()}catch(w){c[e]=d[e];o="javascript:var d="+g+".open();d.domain='"+d.domain+"';";b[k]=o+"void(0);"}try{var t=b.contentWindow[g];t.write(p());t.close()}catch(x){b[k]=o+'d.write("'+p().replace(/"/g,String.fromCharCode(92)+'"')+'");d.close();'}a.P(2)};ld()};nt()})({loader: "static.olark.com/jsclient/loader0.js",name:"olark",methods:["configure","extend","declare","identify"]}); 26 | window.olark.identify(this.settings.siteId); 27 | }, 28 | 29 | 30 | // Identify 31 | // -------- 32 | 33 | // Olark isn't an analytics service, but we can use the `userId` and 34 | // `traits` to tag the user with their real name in the chat console. 35 | identify : function (userId, traits) { 36 | // Choose the best name for the user that we can get. 37 | var name = userId; 38 | if (traits && traits.email) name = traits.email; 39 | if (traits && traits.name) name = traits.name; 40 | if (traits && traits.name && traits.email) name += ' ('+traits.email+')'; 41 | 42 | // If we ended up with no name after all that, get out of there. 43 | if (!name) return; 44 | 45 | window.olark('api.chat.updateVisitorNickname', { 46 | snippet : name 47 | }); 48 | }, 49 | 50 | 51 | // Track 52 | // ----- 53 | 54 | // Again, all we're doing is logging events the user triggers to the chat 55 | // console, if you so desire it. 56 | track : function (event, properties) { 57 | // Check the `track` setting to know whether log events or not. 58 | if (!this.settings.track) return; 59 | 60 | // To stay consistent with olark's default messages, it's all lowercase. 61 | window.olark('api.chat.sendNotificationToOperator', { 62 | body : 'visitor triggered "'+event+'"' 63 | }); 64 | }, 65 | 66 | 67 | // Pageview 68 | // -------- 69 | 70 | // Again, not analytics, but we can mimic the functionality Olark has for 71 | // normal pageviews with pseudo-pageviews, telling the operator when a 72 | // visitor changes pages. 73 | pageview : function () { 74 | // Check the `pageview` settings to know whether they want this or not. 75 | if (!this.settings.pageview) return; 76 | 77 | // To stay consistent with olark's default messages, it's all lowercase. 78 | window.olark('api.chat.sendNotificationToOperator', { 79 | body : 'looking at ' + window.location.href 80 | }); 81 | } 82 | 83 | }); 84 | 85 | 86 | -------------------------------------------------------------------------------- /test/libs/mocha/mocha-1.7.4.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | body { 3 | font: 20px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif; 4 | padding: 60px 50px; 5 | } 6 | 7 | #mocha ul, #mocha li { 8 | margin: 0; 9 | padding: 0; 10 | } 11 | 12 | #mocha ul { 13 | list-style: none; 14 | } 15 | 16 | #mocha h1, #mocha h2 { 17 | margin: 0; 18 | } 19 | 20 | #mocha h1 { 21 | margin-top: 15px; 22 | font-size: 1em; 23 | font-weight: 200; 24 | } 25 | 26 | #mocha h1 a { 27 | text-decoration: none; 28 | color: inherit; 29 | } 30 | 31 | #mocha h1 a:hover { 32 | text-decoration: underline; 33 | } 34 | 35 | #mocha .suite .suite h1 { 36 | margin-top: 0; 37 | font-size: .8em; 38 | } 39 | 40 | .hidden { 41 | display: none; 42 | } 43 | 44 | #mocha h2 { 45 | font-size: 12px; 46 | font-weight: normal; 47 | cursor: pointer; 48 | } 49 | 50 | #mocha .suite { 51 | margin-left: 15px; 52 | } 53 | 54 | #mocha .test { 55 | margin-left: 15px; 56 | } 57 | 58 | #mocha .test.pending:hover h2::after { 59 | content: '(pending)'; 60 | font-family: arial; 61 | } 62 | 63 | #mocha .test.pass.medium .duration { 64 | background: #C09853; 65 | } 66 | 67 | #mocha .test.pass.slow .duration { 68 | background: #B94A48; 69 | } 70 | 71 | #mocha .test.pass::before { 72 | content: '✓'; 73 | font-size: 12px; 74 | display: block; 75 | float: left; 76 | margin-right: 5px; 77 | color: #00d6b2; 78 | } 79 | 80 | #mocha .test.pass .duration { 81 | font-size: 9px; 82 | margin-left: 5px; 83 | padding: 2px 5px; 84 | color: white; 85 | -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 86 | -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 87 | box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 88 | -webkit-border-radius: 5px; 89 | -moz-border-radius: 5px; 90 | -ms-border-radius: 5px; 91 | -o-border-radius: 5px; 92 | border-radius: 5px; 93 | } 94 | 95 | #mocha .test.pass.fast .duration { 96 | display: none; 97 | } 98 | 99 | #mocha .test.pending { 100 | color: #0b97c4; 101 | } 102 | 103 | #mocha .test.pending::before { 104 | content: '◦'; 105 | color: #0b97c4; 106 | } 107 | 108 | #mocha .test.fail { 109 | color: #c00; 110 | } 111 | 112 | #mocha .test.fail pre { 113 | color: black; 114 | } 115 | 116 | #mocha .test.fail::before { 117 | content: '✖'; 118 | font-size: 12px; 119 | display: block; 120 | float: left; 121 | margin-right: 5px; 122 | color: #c00; 123 | } 124 | 125 | #mocha .test pre.error { 126 | color: #c00; 127 | max-height: 300px; 128 | overflow: auto; 129 | } 130 | 131 | #mocha .test pre { 132 | display: inline-block; 133 | font: 12px/1.5 monaco, monospace; 134 | margin: 5px; 135 | padding: 15px; 136 | border: 1px solid #eee; 137 | border-bottom-color: #ddd; 138 | -webkit-border-radius: 3px; 139 | -webkit-box-shadow: 0 1px 3px #eee; 140 | -moz-border-radius: 3px; 141 | -moz-box-shadow: 0 1px 3px #eee; 142 | } 143 | 144 | #mocha .test h2 { 145 | position: relative; 146 | } 147 | 148 | #mocha .test a.replay { 149 | position: absolute; 150 | top: 3px; 151 | right: -20px; 152 | text-decoration: none; 153 | vertical-align: middle; 154 | display: block; 155 | width: 15px; 156 | height: 15px; 157 | line-height: 15px; 158 | text-align: center; 159 | background: #eee; 160 | font-size: 15px; 161 | -moz-border-radius: 15px; 162 | border-radius: 15px; 163 | -webkit-transition: opacity 200ms; 164 | -moz-transition: opacity 200ms; 165 | transition: opacity 200ms; 166 | opacity: 0.2; 167 | color: #888; 168 | } 169 | 170 | #mocha .test:hover a.replay { 171 | opacity: 1; 172 | } 173 | 174 | #mocha-report.pass .test.fail { 175 | display: none; 176 | } 177 | 178 | #mocha-report.fail .test.pass { 179 | display: none; 180 | } 181 | 182 | #mocha-error { 183 | color: #c00; 184 | font-size: 1.5 em; 185 | font-weight: 100; 186 | letter-spacing: 1px; 187 | } 188 | 189 | #mocha-stats { 190 | position: fixed; 191 | top: 15px; 192 | right: 10px; 193 | font-size: 12px; 194 | margin: 0; 195 | color: #888; 196 | } 197 | 198 | #mocha-stats .progress { 199 | float: right; 200 | padding-top: 0; 201 | } 202 | 203 | #mocha-stats em { 204 | color: black; 205 | } 206 | 207 | #mocha-stats a { 208 | text-decoration: none; 209 | color: inherit; 210 | } 211 | 212 | #mocha-stats a:hover { 213 | border-bottom: 1px solid #eee; 214 | } 215 | 216 | #mocha-stats li { 217 | display: inline-block; 218 | margin: 0 5px; 219 | list-style: none; 220 | padding-top: 11px; 221 | } 222 | 223 | code .comment { color: #ddd } 224 | code .init { color: #2F6FAD } 225 | code .string { color: #5890AD } 226 | code .keyword { color: #8A6343 } 227 | code .number { color: #2F6FAD } 228 | -------------------------------------------------------------------------------- /test/libs/sinon-chai/sinon-chai-2.2.0.js: -------------------------------------------------------------------------------- 1 | (function (sinonChai) { 2 | "use strict"; 3 | 4 | // Module systems magic dance. 5 | 6 | if (typeof require === "function" && typeof exports === "object" && typeof module === "object") { 7 | // NodeJS 8 | module.exports = sinonChai; 9 | } else if (typeof define === "function" && define.amd) { 10 | // AMD 11 | define(function () { 12 | return sinonChai; 13 | }); 14 | } else { 15 | // Other environment (usually