diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 000000000..71e55be47 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,46 @@ +version: 2 +workflows: + version: 2 + run: + jobs: + - build +jobs: + build: + working_directory: ~/repo + environment: + BASH_ENV: ~/.nvm/nvm.sh + docker: + - image: google/cloud-sdk + steps: + - checkout + - run: + name: Download nvm + command: curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.0/install.sh | bash + - run: + name: Install nvm v10.16.3 + command: | + nvm install v10.16.3 + nvm alias default v10.16.3 + - run: + name: Install root dependencies + command: npm install + - run: + name: Install lib dependencies + working_directory: lib + command: npm install + - run: + name: Set gcloud key & project + command: | + echo $GCLOUD_SERVICE_KEY > ${HOME}/gcp-key.json + gcloud --quiet config set project api-dreamdata-cloud + gcloud --quiet auth activate-service-account --key-file=${HOME}/gcp-key.json + - run: + name: Build analytics.min.js + command: npm run build:prod + - run: + name: Copy analytics.min.js to CDN bucket with 5min cache + command: | + gsutil -h "Cache-Control:public,max-age=1800" \ + -h "Content-Type:text/javascript; charset=utf-8" \ + -h "Content-Encoding:gzip" \ + cp dist/analytics.min.js.gz gs://cdn-dreamdata-cloud-eu/scripts/analytics/v1/dreamdata.min.js diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..b512c09d4 --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/.gitignore b/.gitignore index 6e3c06b91..bb6fa4d53 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,104 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories node_modules/ -test/server/pid.txt -components -build.js +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test + +# parcel-bundler cache (https://parceljs.org/) +.cache + +# Next.js build output +.next + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and *not* Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 000000000..8cdad2c80 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,14 @@ +{ + "arrowParens": "avoid", + "bracketSpacing": true, + "jsxBracketSameLine": true, + "jsxSingleQuote": true, + "printWidth": 130, + "proseWrap": "preserve", + "quoteProps": "as-needed", + "semi": true, + "singleQuote": true, + "tabWidth": 4, + "trailingComma": "none", + "useTabs": false +} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 99bb1fd05..000000000 --- a/.travis.yml +++ /dev/null @@ -1,18 +0,0 @@ -language: node_js -script: make test-sauce -node_js: -- '0.12' -env: - global: - - secure: LoNu6uwsubuGa63PgPfXDlNSOdJ7PdPlk3hgadnAdYpskrDtg4op7uNREpf6ZYbJEKHmCMfgN7bqPEakw3Lnfw0f+HAteJmRdc2TJ6Ntq+ZF8H7hDdD7vWx6PUOK2VuDyNNRkWj1Imxm4ZF+TCgItdNFVjn8RifwVqsXIgCZPLs= - - secure: CkzgZbbMfR2LgKvigLs8kSgXQYZw8w1f9NrFoppTTEbJD13EiVj7FWF9EH4PFvINEeuFZb3f0lvwCq2PGpMtl5ckyfXXoDdNNUTkJbL5D6wm9pkKu09G74LZ20vT/U5HIs+o7/E2ARRrhH/nTY2rJysuDlcjkZ3lkDm5CIG888k= - - secure: ixFsGpkqQty4f8kTOZFq+BF3nmkMCHqQKCaXdgtOEADJXpRoqWtGnAgM1EXF0fDsmf825E+gxwJbBk7lBgCykOnToVmXsoNoPyLA8c/rjWVwFBwFcrRidaR7qbAbWQr1qGxEWwveP5ijj8neEXs9cslwv1y31WCBjXaz/ZHvEnI= - - secure: F5GN7Ph+7qcM/QPekFuaOZzzRA4+NpfdmVxdQPJZuEy4GTAdM3USzPBuYebxji6DQjplGXM0szUIZPuwWJowHVzUvbY4OuwYHGbIJm0Sc9ZR78DFCPIMIZ577rliqybvpKWjl9eXGacre8sa5UzQdL7z0CI8xDzdrmHnr23hkeY= - matrix: - - BROWSER=chrome - - BROWSER=firefox - - BROWSER=safari - - BROWSER=ie:11 - - BROWSER=ie:10 - - BROWSER=ie:9 - - BROWSER=ie:8 diff --git a/History.md b/History.md deleted file mode 100644 index 394d8d86a..000000000 --- a/History.md +++ /dev/null @@ -1,1662 +0,0 @@ - -2.11.0 / 2015-08-17 -================== - - * Add facebook-custom-audiences integration - - -2.10.2 / 2015-08-17 -================== - - * fix analytics.js singleton export - -2.10.0 / 2015-06-30 -=================== - - * Move core library out to segmentio/analytics.js-core - - This repository is now purely a build repository for Analytics.js. For analytics.js's core library, see [segmentio/analytics.js-core](https://github.com/segmentio/analytics.js-core); for integrations, see the [segment-integrations](https://github.com/segment-integrations) organization. - -2.9.1 / 2015-06-11 -================== - - * Remove deprecated analytics.js-integrations dependency - * Update build - -2.9.0 / 2015-06-11 -================== - - * Pull integrations from individual repositories, located in the [segment-integrations GitHub organization](https://github.com/segment-integrations/). This change should be unnoticeable from a user perspective, but has huge benefits in that excluding integrations from custom builds is now much, much easier, and one integration's test failures will no longer prevent another integration's tests from running. - - A noteworthy part of this change: All integrations are now pulled into Analytics.js in `component.json`, using an explicit version number. -In the future this part of the build process is very likely to change to be more of an automatic process, but for now--baby steps. - -2.8.25 / 2015-06-03 -=================== - - * Update build (for real this time) - -2.8.24 / 2015-06-03 -=================== - - * Update build - -2.8.23 / 2015-05-27 -=================== - - * Update component/url dependency to 0.2.0 - -2.8.22 / 2015-05-22 -=================== - - * Update build - -2.8.21 / 2015-05-22 -=================== - - * Update build - -2.8.20 / 2015-05-22 -=================== - - * Update build - * Clean up Makefile - -2.8.19 / 2015-05-16 -=================== - - * Pin all dependencies - * Bump Node.js engine dependency to 0.12 - -2.8.18 / 2015-05-14 -=================== - - * Bump duo-test dependency - -2.8.17 / 2015-05-02 -=================== - - * Build updated - -2.8.16 / 2015-05-01 -=================== - - * Build updated - -2.8.15 / 2015-04-29 -=================== - - * Build updated - -2.8.14 / 2015-04-29 -=================== - - * Build updated - -2.8.13 / 2015-04-28 -=================== - - * Build updated - -2.8.12 / 2015-04-23 -=================== - - * deps: bump top-domain for test cookie deletion fix - * cookie: bump top-domain to v2 to catch all top domains - - -2.8.10 / 2015-04-20 -=================== - - * Build updated - -2.8.9 / 2015-04-16 -================== - * Fix conflicts - -2.8.8 / 2015-04-16 -================== - - * Updating analytics.js-integrations - * Updating analytics.js-integrations - -2.8.7 / 2015-04-09 -================== - - * Build updated - * adding pre-release hook (and make targets for hooks) - -2.8.6 / 2015-04-09 -================== - - * Build updated - -2.8.5 / 2015-04-09 -================== - - * Build updated - -2.8.4 / 2015-04-02 -================== - - * Build updated - -2.8.3 / 2015-03-31 -================== - - * Build updated - -2.8.2 / 2015-03-24 -================== - - * Build updated - -2.8.1 / 2015-03-20 -================== - - * Build updated - * adding a build phony target - -2.8.0 / 2015-03-07 -================== - - * group: fix typo - * entity: add debug warning for memory store - * test: add fallback to memory tests - * entity: fallback to memory - * add memory store - * entity: fallback to localstorage when cookies are disabled - * tests: add localstorage fallback tests - * dist: rebuild - -2.7.1 / 2015-03-05 -================== - - * Updating analytics.js-integrations - -2.7.0 / 2015-03-05 -================== - - * Attach page metadata to all calls as `context.page` - -2.6.13 / 2015-03-04 -=================== - - * normalize: remove trailing comma - * dist: rebuild to make tests pass - * normalize: remove redundant keys from toplevel - -2.6.12 / 2015-03-03 -=================== - - * Release 2.6.11 - * normalize: keep traits in options - -2.6.11 / 2015-02-28 -================== - - * normalize: keep traits in options - -2.6.10 / 2015-02-25 -=================== - - * Updating analytics.js-integrations - -2.6.9 / 2015-02-25 -================== - - * Updating analytics.js-integrations - -2.6.8 / 2015-02-25 -================== - - * Updating analytics.js-integrations - -2.6.7 / 2015-02-24 -================== - - * Updating analytics.js-integrations - * removed duplicate .on('initialize') from analytics constructor - -2.6.6 / 2015-02-23 -================== - - * update integrations - - -2.6.5 / 2015-02-19 -================== - - * analytics: less verbose logging - * analytics.js: cleanup plan - * analytics.js: add debugs - * normalize: dont clobber and add tests - * analytics: use normalize removing message() - * add normalize.js - - -2.6.4 / 2015-02-19 -================== - - * Updating analytics.js-integrations - -2.6.3 / 2015-02-17 -================== - - * plan: .archived -> .enabled - -2.6.1 / 2015-02-12 -================== - - * user: fix old anonymous id - - -2.6.0 / 2015-02-09 -================== - - * .track(): ignore archived events - * ._options(): preserve options - -2.5.17 / 2015-02-04 -=================== - - * Updating analytics.js-integrations - -2.5.16 / 2015-02-04 -=================== - - * Updating analytics.js-integrations - -2.5.15 / 2015-02-03 -=================== - - * Updating analytics.js-integrations - -2.5.14 / 2015-02-03 -=================== - - * Updating analytics.js-integrations - -2.5.13 / 2015-01-29 -=================== - - * Updating analytics.js-integrations - -2.5.12 / 2015-01-23 -=================== - - * Updating analytics.js-integrations - -2.5.10 / 2015-01-22 -=================== - - * Updating analytics.js-integrations - -2.5.9 / 2015-01-22 -================== - - * Updating analytics.js-integrations - -2.5.8 / 2015-01-21 -================== - - * Updating analytics.js-integrations - -2.5.7 / 2015-01-15 -================== - - * Updating analytics.js-integrations - -2.5.6 / 2015-01-15 -================== - - * Updating analytics.js-integrations - -2.5.5 / 2015-01-14 -================== - - * Updating analytics.js-integrations - -2.5.4 / 2015-01-14 -================== - - * Updating analytics.js-integrations - -2.5.3 / 2015-01-08 -================== - - * Fix release - -2.5.2 / 2015-01-08 -================== - - * Updating analytics.js-integrations - -2.5.0 / 2015-01-01 -================== - - * update integrations - * analytics: add setAnonymousId - -2.4.21 / 2014-12-11 -=================== - - * Updating analytics.js-integrations - * tests: skip svg tests on legacy browsers - * travis: node 0.11.13 - * trackLink: support svg anchor tags - * add cross browser tests - -2.4.18 / 2014-11-22 -=================== - - * Updating analytics.js-integrations - -2.4.16 / 2014-11-13 -=================== - - * Updating analytics.js-integrations - -2.4.15 / 2014-11-11 -================== - - * clean: --force to ignore errs - * Updating analytics.js-integrations - -2.4.14 / 2014-11-06 -=================== - - * Updating analytics.js-integrations - -2.4.10 / 2014-10-27 -================== - - * support umd - -2.4.9 / 2014-10-25 -================== - - * Updating analytics.js-integrations - -2.4.7 / 2014-10-21 -================== - - * Updating analytics.js integrations to 1.3.2 - -2.4.6 / 2014-10-17 -================== - - * upgrade integrations to 2.3.1 - -2.4.5 / 2014-10-17 -================== - - * upgrade integrations to 2.3 - -2.4.4 / 2014-10-16 -================== - - * upgrade integrations. - -2.4.3 / 2014-10-15 -================== - - * Merge pull request #407 from segmentio/prevent/duplicates - * fix: prevent duplicates when cookie cannot be set - -2.4.2 / 2014-10-15 -================== - - * Merge pull request #406 from segmentio/fix/user-id-reset - * fix: prevent anonymousId from changing when user id is reset - -2.4.1 / 2014-10-14 -================== - - * Merge pull request #405 from segmentio/fix/old-anonymous-id - * fix: old anonymousId is not stringified, use raw cookie - * Release 2.4.0 - -2.4.0 / 2014-10-14 -================== - - * anonymousId: re-generate when user id changes - * Merge pull request #401 from segmentio/anonymous-id - * analytics.reset(): use .logout() to preserve options - * logout: remove anonymous id - * parseQuery: add ajs_aid - * analytics add anonymousId support - * add User#anonymousId - * Release 2.3.33 - -2.3.33 / 2014-10-14 -=================== - - * upgrade integrations - -2.3.33 / 2014-10-10 -================== - - * upgrade integrations - -2.3.32 / 2014-10-09 -=================== - - * upgrade integrations - -2.3.31 / 2014-10-08 -=================== - - * history.md: ocd - -2.3.30 / 2014-10-07 -=================== - - * upgrade integrations - -2.3.29 / 2014-10-06 -=================== - - * add reset(), closes #378 - -2.3.28 / 2014-10-01 -=================== - - * upgrade integrations - - -2.3.27 / 2014-09-26 -=================== - - * upgrade integrations - - -2.3.26 / 2014-09-26 -=================== - - * upgrade integrations - - -2.3.25 / 2014-09-22 -=================== - - * add node 0.11 notice for now - -2.3.24 / 2014-09-17 -=================== - - * upgrade integrations - - -2.3.23 / 2014-09-08 -=================== - - * upgrade integrations - - -2.3.22 / 2014-09-05 -=================== - - * upgrade integrations - - -2.3.21 / 2014-09-04 -=================== - - * ocd - -2.3.20 / 2014-09-04 -=================== - - * upgrade integrations - - -2.3.19 / 2014-09-02 -=================== - - * upgrade integrations - - -2.3.18 / 2014-09-02 -=================== - - * upgrade integrations - - -2.3.17 / 2014-08-28 -=================== - - * deps: duo 0.7 - * deps: duo 0.7 - * Merge pull request #397 from segmentio/add/anonymous-id - * add checking for anonymous id in options - -2.3.15 / 2014-08-22 -================== - - * google adwords: directly pass remarketing option - -2.3.14 / 2014-08-22 -================== - - * deps: upgrade to duo-test@0.3.x - * google adwords: switch to async api - -2.3.13 / 2014-08-20 -================== - - * localstorage fallback: add implementation - * localstorage fallback: add tests - * rebuild - * deps: upgrade to duo 0.7 - * make: dont clean my npm cache :P - -2.3.12 / 2014-08-07 -================== - - * remove userfox - -2.3.11 / 2014-08-07 -================== - - * merge a few more fixes (keen.io) - -2.3.10 / 2014-07-25 -================== - - * Make lots of analytics.js-integrations fixes - -2.3.7 / 2014-07-21 -================== - - * Merge pull request #390 from segmentio/test/element-error - * throw helpful error when passing string to `trackLink`, closes #389 - * Merge pull request #386 from segmentio/context - * add integrations select test - * add backwards compat options object support - -2.3.6 / 2014-07-16 -================== - - * upgrade integrations - - -2.3.5 / 2014-07-16 -================== - - * upgrade integrations - - -2.3.4 / 2014-07-16 -================== - - * upgrade integrations - - -2.3.3 / 2014-07-16 -================== - - * fix: History.md - -2.3.2 / 2014-07-13 -================== - -* rebuild - -2.3.1 / 2014-07-13 -================== - - * deps: remove duo-package - * make: test-saucelabs -> test-sauce - -2.3.0 / 2014-07-11 -================== - - * use analytics.js-integrations 1.2.0 which removes plugin.Integration - * set .analytics on integration instance - -2.2.5 / 2014-07-08 -================== - - * loosen deps - -2.2.4 / 2014-07-08 -=================== - - * rebuild - -2.2.3 / 2014-07-07 -=================== - - * rebuild - -2.2.2 / 2014-06-24 -================== - -* fix fxn - -2.2.1 / 2014-06-24 -================== - -* fix typo - -2.2.0 / 2014-06-24 -================== - -* bump analytics.js-integrations with bing/bronto fixes - -2.1.0 / 2014-06-23 -================== - - * add `.add` for test-friendliness - * make-test: kill the server when done testing - * tests: add reporter option - * update readme - * make-test: make sure we use the correct phantomjs(1) - -2.0.1 / 2014-06-13 -================== - - * bumping store.js dep to 2.0.0 - * update readme - -2.0.0 / 2014-06-12 -================== - - * converting to use duo - -1.5.12 / 2014-06-11 -================== - -* bump analytics.js-integrations to 0.9.9 - -1.5.11 / 2014-06-05 -================== - -* bump analytics.js-integrations to 0.9.8 - -1.5.10 / 2014-06-04 -================== - - * bump analytics.js-integrations to 0.9.7 - -1.5.9 / 2014-06-04 -================== - - * bump analytics.js-integrations to 0.9.6 - -1.5.8 / 2014-06-04 -================== - - * bump analytics.js-integrations to 0.9.5 - -1.5.6 / 2014-06-02 -================== - - * bump analytics.js-integrations to 0.9.3 - -1.5.5 / 2014-06-02 -================== - - * bump analytics.js-integrations to 0.9.2 - -1.5.4 / 2014-05-30 -================== - - * upgrade integrations to 0.9.1 - -1.5.3 / 2014-05-29 -================== - - * upgrade integrations to 0.9.0 - -1.5.1 / 2014-05-20 -================== - - * update analytics.js-integrations dep for reverting KISSmetrics fixes - -1.5.0 / 2014-05-19 -================== - - * updating analytics.js-integrations to 0.8.0 for KISSmetrics fixes - -1.4.0 / 2014-05-17 -================== - - * upgrade integrations to 0.7.0 - * upgrade facade to 0.3.10 - -1.3.31 / 2014-05-17 -================== - - * handle dev envs correctly, closes #359 - -1.3.30 / 2014-05-07 -================== - - * upgrade integrations to 0.6.1 for google analytics custom dimensions and metrics - -1.3.28 / 2014-04-29 -================== - - * upgrade integrations to 0.5.10 for navilytics fix and mixpanel fix - * component: upgrade to 0.19.6 and add githubusercontent to remotes - -1.3.26 / 2014-04-17 -================== - - * upgrade integrations to 0.5.8 - -1.3.25 / 2014-04-16 -================== - - * upgrade integrations to 0.5.6 - -1.3.24 / 2014-04-15 -================== - - * move analytics.js-integration to dev deps - -1.3.23 / 2014-04-14 -================== - - * upgrade integrations to 0.5.5 - * update querystring to 1.3.0 - -1.3.22 / 2014-04-11 -================== - - * upgrade integrations to 0.5.4 - -1.3.21 / 2014-04-10 -================== - - * add "invoke" event - -1.3.20 / 2014-04-07 -================== - - * upgrade integrations to 0.5.3 - -1.3.19 / 2014-04-05 -================== - - * upgrade querystring to 1.2.0 - -1.3.18 / 2014-04-05 -================== - - * upgrade integrations to 0.5.1 - -1.3.17 / 2014-04-04 -================== - - * upgrade integrations to 0.5.0 - * fix: add .search to .url when url is pulled from canonical tag - * tests: upgrade gravy to 0.2.0 - -1.3.16 / 2014-04-01 -================== - - * upgrade integrations to 0.4.14 - -1.3.15 / 2014-03-26 -================== - - * upgrade integrations to 0.4.13 - -1.3.14 / 2014-03-26 -================== - - * upgrade integrations to 0.4.12 - -1.3.13 / 2014-03-25 -================== - - * upgrade integrations to 0.4.11 - -1.3.12 / 2014-03-19 -================== - - * upgrade integrations to 0.4.10 - -1.3.11 / 2014-03-14 -=================== - - * upgrade integrations to 0.4.9 - -1.3.10 / 2014-03-14 -=================== - - * upgrade integrations to 0.4.8 - -1.3.9 / 2014-03-14 -================== - - * upgrade integrations to 0.4.7 - -1.3.8 / 2014-03-13 -================== - - * upgrade integrations to 0.4.6 - -1.3.7 / 2014-03-06 -================== - - * upgrade integrations to 0.4.5 - * upgrade facade to 0.2.11 - -1.3.6 / 2014-03-05 -================== - - * upgrade integrations to 0.4.4 - -1.3.4 / 2014-02-26 -================== - - * update integrations to 0.4.2 - -1.3.3 / 2014-02-18 -================== - - * upgrade analytics.js-integrations to 0.4.1 - * dont reset ids and traits - -1.3.2 / 2014-02-07 -================== - - * upgrade analytics.js-integrations to 0.4.0 - * upgrade analytics.js-integration to 0.1.7 - * upgrade facade to 0.2.7 - * fix page url default to check canonical and remove hash - -1.3.1 / 2014-01-30 -================== - - * upgrade isodate-traverse to `0.3.0` - * upgrade facade to `0.2.4` - * upgrade analytics.js-integrations to `0.3.10` - -1.3.0 / 2014-01-23 -================== - - * update analytics.js-integrations to 0.3.9 - -1.2.9 / 2014-01-18 -================== - -* update `analytics.js-integrations` to `0.3.8` -* expose `require()` - -1.2.8 / 2014-01-15 -================== - -* update `analytics.js-integrations` to `0.3.7` -* upgrade `facade` to `0.2.3` - -1.2.7 / 2014-01-10 -================== - - * update `analytics.js-integrations` to `0.3.6` - -1.2.6 - January 3, 2014 ------------------------ -* upgrade `component(1)` for json support - -1.2.5 - January 3, 2014 ------------------------ -* upgrade `analytics.js-integrations` to `0.3.5` -* upgrade `facade` to `0.2.1` - -1.2.4 - January 2, 2014 -------------------------- -* upgrade `analytics.js-integrations` to `0.3.4` - -1.2.3 - December 18, 2013 -------------------------- -* fix `facade` dependency - -1.2.2 - December 18, 2013 -------------------------- -* upgrade `analytics.js-integrations` to `0.3.2` - -1.2.1 - December 16, 2013 -------------------------- -* add #push, fixes #253 - -1.2.0 - December 13, 2013 -------------------------- -* add [`facade`](https://github.com/segmentio/facade) - -1.1.9 - December 11, 2013 -------------------------- -* upgrade `analytics.js-integrations` to `0.2.16` -* add `search` to page property defaults - -1.1.8 - December 11, 2013 ------------------------- -* upgrade `analytics.js-integrations` to `0.2.15` -* add [WebEngage](http://webengage.com) -* heap: fallback to user id as handle - -1.1.7 - December 4, 2013 ------------------------- -* upgrade `analytics.js-integrations` to `0.2.13` - -1.1.6 - December 2, 2013 ------------------------- -* update `analytics.js-integrations` to `0.2.12` -* add `entity` -* change `user` to inherit from `entity` -* change `group` to inherit from `entity` - -1.1.5 - November 26, 2013 -------------------------- -* update `analytics.js-integration` to `0.1.5` -* update `analytics.js-integrations` to `0.2.11` - -1.1.4 - November 25, 2013 -------------------------- -* fix `page` method properties overload - -1.1.3 - November 21, 2013 -------------------------- -* update `analytics.js-integrations` to `0.2.10` - -1.1.2 - November 21, 2013 -------------------------- -* update `analytics.js-integrations` to `0.2.9` - -1.1.1 - November 20, 2013 -------------------------- -* update `analytics.js-integrations` to `0.2.8` - -1.1.0 - November 20, 2013 -------------------------- -* add `name` and `category` defaults to `page` method calls -* update `analytics.js-integrations` to `0.2.7` - -1.0.9 - November 15, 2013 -------------------------- -* update `analytics.js-integrations` to `0.2.6` -* update dependencies - -1.0.8 - November 14, 2013 -------------------------- -* update `analytics.js-integrations` to `0.2.5` - -1.0.7 - November 13, 2013 ------------------------- -* update `analytics.js-integrations` to `0.2.4` - -1.0.6 - November 12, 2013 -------------------------- -* update `analytics.js-integrations` to `0.2.3` -* update `analytics.js-integration` to `0.1.4` - -1.0.5 - November 12, 2013 -------------------------- -* update `analytics.js-integrations` to `0.2.2` -* fix `properties` overload for `page` method - -1.0.4 - November 12, 2013 -------------------------- -* update `analytics.js-integrations` to `0.2.1` - -1.0.3 - November 11, 2013 -------------------------- -* update `analytics.js-integrations` to `0.2.0` - -1.0.2 - November 11, 2013 -------------------------- -* rename the page methods `section` argument to `category` -* update `analytics.js-integration` -* update `analytics.js-integrations` - -1.0.1 - November 11, 2013 -------------------------- -* change `page` to take a `section` -* update `analytics.js-integration` -* update `analytics.js-integrations` - -1.0.0 - November 10, 2013 -------------------------- -* change `pageview` method to `page` -* add call to `page` as mandatory to initialize some analytics tools -* remove ability to `initialize` by `key` -* add checking for an integration already being loaded before loading -* add `#use` method for plugins -* add event emitter to `analytics` -* move integrations to [`analytics.js-integrations`](https://github.com/segmentio/analytics.js-integrations) -* add debugging to all integrations -* move integration factory to [`analytics.js-integration`](https://github.com/segmentio/analytics.js-integration) -* Amplitude: rename `pageview` option to `trackAllPages` -* Amplitude: add `trackNamedPages` option -* Google Analytics: add `trackNamedPages` option -* Google Analytics: remove `initialPageview` option -* Keen IO: rename `pageview` option to `trackAllPages` -* Keen IO: add `trackNamedPages` option -* Keen IO: remove `initialPageview` option -* Lytics: remove `initialPageview` option -* Mixpanel: rename `pageview` option to `trackAllPages` -* Mixpanel: add `trackNamedPages` option -* Mixpanel: remove `initialPageview` option -* Olark: rename `pageview` option to `page` -* Tapstream: remove `initialPageview` option -* Tapstream: add `trackAllPages` option -* Tapstream: add `trackNamedPages` option -* Trak.io: remove `pageview` option -* Trak.io: remove `initialPageview` option -* Trak.io: add `trackNamedPages` option -* Woopra: remove `initialPageview` option - -0.18.4 - October 29, 2013 -------------------------- -* adding convert-date 0.1.0 support - -0.18.3 - October 29, 2013 -------------------------- -* hubspot: adding fix for date traits/properties (calvinfo) - -0.18.2 - October 28, 2013 -------------------------- -* upgrade visionmedia/debug to most recent version, fixes security warnings when cookies are disabled. - -0.18.1 - October 28, 2013 -------------------------- -* add [Evergage](http://evergage.com), by [@glajchs](https://github.com/glajchs) - -0.18.0 - October 24, 2013 -------------------------- -* add event emitter -* add `initialize`, `ready`, `identify`, `alias`, `pageview`, `track`, and `group` events and tests -* fix date equality tests - -0.17.9 - October 24, 2013 -------------------------- -* Google Analytics: fix ip anonymization should come after `create` -* Google Analytics: fix domain to default to `"none"` - -0.17.8 - October 14, 2013 -------------------------- -* Customer.io: added preliminary `group` support - -0.17.7 - October 10, 2013 -------------------------- -* propagating traverse isodate fix - -0.17.6 - October 7, 2013 ------------------------- -* added [Yandex Metrica](http://metrika.yandex.com), by [@yury-egorenkov](https://github.com/yury-egorenkov) - -0.17.5 - October 2, 2013 ------------------------- -* fixed bug in `_invoke` not cloning arguments - -0.17.4 - September 30, 2013 ---------------------------- -* added conversion of ISO strings to dates for `track` calls - -0.17.3 - September 30, 2013 ---------------------------- -* fixed bug in key-only initialization - -0.17.2 - September 30, 2013 ---------------------------- -* UserVoice: added `classicMode` option - -0.17.1 - September 30, 2013 ---------------------------- -* UserVoice: fixed bug loading trigger with new widget - -0.17.0 - September 30, 2013 ---------------------------- -* added `debug` method, by [@yields](https://github.com/yields) - -0.16.0 - September 27, 2013 ---------------------------- -* UserVoice: updated integration to handle the new widget - -0.15.2 - September 26, 2013 ---------------------------- -* added Awesomatic, by [@robv](https://github.com/robv) - -0.15.1 - September 24, 2013 ---------------------------- -* fixed bug in `ready` causing it to never fire with faulty settings -* fixed all `ready()` calls to always be async -* cleared ready state after all analytics core `initialize` tests - -0.15.0 - September 18, 2013 ---------------------------- -* Crazy Egg: renamed from `CrazyEgg` -* Google Analytics: changed `universalClient` option to `classic` -* Google Analytics: changed `classic` default to `false` -* Keen IO: changed pageview options defaults to `false` -* LeadLander: changed `llactid` option to human-readable `accountId`* Intercom: make `#IntercomDefaultWidget` the default activator - -0.14.3 - September 18, 2013 ---------------------------- -* exposed `createIntegration` and `addIntegration` - -0.14.2 - September 17, 2013 ---------------------------- -* added [Spinnakr](http://spinnakr.com) - -0.14.1 - September 17, 2013 ---------------------------- -* removed old `Provider` for an `integration` factory - -0.14.0 - September 16, 2013 ---------------------------- -* exposed `group` via the `#group` method -* exposed `user` via the `#user` method -* started caching `group` in cookie and local storage like `user` -* changed `user` and `group` info to always be queried from storage -* bound all `analytics` methods as a singleton -* added `identify(traits, options)` override -* added `timeout` setter method - -0.13.2 - September 16, 2013 ---------------------------- -* added [Rollbar](https://rollbar.com/), by [@coryvirok](https://github.com/coryvirok) - -0.13.1 - September 12, 2013 ---------------------------- -* Olark: added tests for empty emails, names and phone numbers - -0.13.0 - September 11, 2013 ---------------------------- -* converted all integrations and their tests to a cleaner format -* renamed all instances of "provider" to "integration" -* built integration list from their own `name` to avoid bugs -* changed `_providers` array to an `_integrations` map - -0.12.2 - September 5, 2013 --------------------------- -* added [awe.sm](http://awe.sm) - -0.12.1 - September 5, 2013 --------------------------- -* UserVoice: fix bug where some installations wouldn't show the tab - -0.12.0 - September 4, 2013 --------------------------- -* Clicky: fixed custom tracking, added `pageview` - -0.11.16 - September 3, 2013 ---------------------------- -* updated `segmentio/new-date` for old browser support -* Woopra: fixed default pageview properties -* Intercom: cleaned up identify logic and tests - -0.11.15 - September 2, 2013 ---------------------------- -* pinned all dependencies -* added [Inspectlet](https://www.inspectlet.com) -* fixed storage options tests -* AdRoll: added custom data tracking - -0.11.14 - August 30, 2013 -------------------------- -* bumped version of [`ianstormtaylor/is`](https://github.com/ianstormtaylor/is) for bugfix - -0.11.13 - August 29, 2013 -------------------------- -* Spinnakr: added global variable for site id -* LeadLander: switched to non `document.write` version -* Customer.io: convert date objects to seconds -* fixed `is.function` bug in old browsers - -0.11.12 - August 27, 2013 -------------------------- -* cleaned up core -* fixed breaking tests -* removed Bitdeli, by @jtuulos -* updated Woopra to use new tracker, by @billyvg -* added trak.io, by @msaspence -* added `createIntegration` interim method -* added more Lytics options, by @slindberg and @araddon -* added trait alias to trak.io -* added MouseStats, by @Koushan -* added Tapstream, by @adambard -* allow Mixpanel to name users by `username` -* allow GoSquared to name users by `email` or `username` -* make Google Analytics ignored referrers an array -* update Errorception cdn - -0.11.11 - August 9, 2013 ------------------------- -* Added LeadLander - -0.11.10 - July 12, 2013 ------------------------ -* Added cookieName to Mixpanel options - 0a53afd - -0.11.9 - June 11, 2013 ----------------------- -* Added [Visual Website Optimizer](http://visualwebsiteoptimizer.com/) - -0.11.8 - June 10, 2013 ----------------------- -* Intercom: added `group` support - -0.11.7 - June 7, 2013 ---------------------- -* Fix for cookie domains, now sets to subdomain friendly by default. -* Renaming bindAll -> bind-all - -0.11.6 - June 6, 2013 ---------------------- -* Added `group` support to Preact by [@azcoov](https://github.com/azcoov) -* Fixed `created` bug with userfox -* Changed to new Vero CDN URL -* Fixed bug when initializing unknown providers -* Added `options` object to `pageview` by [@debangpaliwal](https://github.com/devangpaliwal) - -0.11.5 - June 3, 2013 ---------------------- -* Adding segmentio/json temporarily, fixing json-fallback - -0.11.4 - May 31, 2013 ---------------------- -* Updated Intercom's library URL - -0.11.3 - May 31, 2013 ---------------------- -* Added trailing comma fix - -0.11.2 - May 30, 2013 ---------------------- -* Added fix for UserVoice displaying `'null'` -* Added `make clean` before running components (fixes json fallback) - -0.11.1 - May 29, 2013 ---------------------- -* Fixed bug with Google Analytics not tracking integer `value`s - -0.11.0 - May 28, 2013 ---------------------- -* Switched from cookie-ing to localStorage - -0.10.6 - May 23, 2013 ---------------------- -* Moved trait parsing logic to the global level -* Added [Improvely](http://www.improvely.com/) -* Added [Get Satisfaction](https://getsatisfaction.com/) -* Added a `$phone` alias for Mixpanel -* Added the ability to pass a function for the `event` to `trackLink` and `trackForm` - -0.10.5 - May 22, 2013 ---------------------- -* Added [Amplitude](https://amplitude.com/) support -* Fixed improperly parsed cookies - -0.10.4 - May 17, 2013 ---------------------- -* Fixed bug with Google Analytics being ready to soon - -0.10.3 - May 15, 2013 ---------------------- -* Added [Optimizely](https://www.optimizely.com) - -0.10.2 - May 14, 2013 ---------------------- -* Fixed handling of `increments` and `userHash` from `options.Intercom` - -0.10.1 - May 14, 2013 ---------------------- -* Added `identify` to SnapEngage integration - -0.10.0 - May 9, 2013 --------------------- -* Added `group` method - -0.9.18 - May 9, 2013 --------------------- -* Added [Preact](http://www.preact.io/) support by [@azcoov](https://github.com/azcoov) - -0.9.17 - May 1, 2013 --------------------- -* Updated Keen to version 2.1.0 - -0.9.16 - April 30, 2013 ------------------------ -* Fixed bug affecting Pingdom users - -0.9.15 - April 30, 2013 ------------------------ -* Added identify to UserVoice - -0.9.14 - April 29, 2013 ------------------------ -* Fixing userfox integration to accept all traits not just signup_date - -0.9.13 - April 29, 2013 ------------------------ -* Fixing ordering of ignore referrer option in Google Analytics - -0.9.12 - April 27, 2013 ------------------------ -* Adding support for [userfox](https://www.userfox.com) - -0.9.11 - April 26, 2013 ------------------------ -* Adding new ignoreReferrer option to Google Analytics provider -* Adding new showFeedbackTab option to BugHerd provider -* Updating UserVoice provider to work with their new snippet(s) -* Fixing Errorception window.onerror binding to be friendlier - -0.9.10 - April 17, 2013 ------------------------ -* Adding url and title to mixpanel pageviews -* Addiung url and title to keen pageviews - -0.9.9 - April 17, 2013 ----------------------- -* Fixed GoSquared relying on `document.body - -0.9.8 - April 16, 2013 ----------------------- -* Adding support for Pingdom RUM -* Adding support for AdRoll - -0.9.7 - April 16, 2013 ----------------------- -* Fixing LiveChat test -* Updating mixpanel snippet to wait for ready until script loads -* Adding full traits pulled in from identify. - -0.9.6 - April 10, 2013 ----------------------- -* Renaming Provider.options to Provider.defaults -* Adding universal analytics support to Google Analytics - -0.9.5 - April 10, 2013 ----------------------- -* Adding support for new Olark Javascript API functions, see #121 - -0.9.4 - April 4, 2013 ---------------------- -* Fixing Uservoice integration -* Fixing ready tests. -* Adding lytics integration by [@araddon](https://github.com/araddon) -* Adding bower support by [@jede](https://github.com/jede) - -0.9.3 - April 2, 2013 ---------------------- -* Olark provider now only notifies the operator of track and pageview when the chat box is expanded. - -0.9.2 - March 28, 2013 ----------------------- -* Qualaroo provider now prefers to identify with traits.email over a non-email userId --- makes the survey responses human readable. - -0.9.1 - March 28, 2013 ----------------------- -* Woopra no longer tracks after each identify so that duplicate page views aren't generated. - -0.9.0 - March 27, 2013 ----------------------- -* Changed default Keen IO settings to record all pageviews by default -* Removed Keen IO API Key option since that is no longer used for data "writes" to their API -* Renamed Keen IO projectId to projectToken to match their docs - -0.8.13 - March 25, 2013 ------------------------ -* Added ability to pass variables into `intercomSettings` via `context.intercom` - -0.8.12 - March 25, 2013 ------------------------ -* Added [Heap](https://heapanalytics.com) - -0.8.11 - March 24, 2013 ------------------------ -* Removed [Storyberg](http://storyberg.com/2013/03/18/the-end.html), best of luck guys - -0.8.10 - March 14, 2013 ------------------- -* Added fix for conversion of `company`'s `created` date -* Added extra tests for `trackForm` -* Fixing issue with ClickTale https bug - -0.8.9 - March 13, 2013 ----------------------- -* Migrated to new Intercom Javascript API -* Removed un-used Intercom traits -* Fix bug in `trackForm` when using jQuery - -0.8.8 - March 12, 2013 ----------------------- -* Added `userId` to Errorception metadata -* Made date parsing more lenient (ms & sec) for trait.created - -0.8.7 - March 7, 2013 ---------------------- -* Added [Qualaroo](https://qualaroo.com/) -* Fixed bug with Chartbeat and page load times - -0.8.6 - March 7, 2013 ---------------------- -* Fixed bug in `trackLink` reported by [@quirkyjack](https://github.com/quirkyjack) -* Fixed bug in ClickTale where it didn't create the ClickTaleDiv - -0.8.5 - March 7, 2013 ---------------------- -* Added [Storyberg](http://storyberg.com/) by [@kevinicus](https://github.com/kevinicus) -* Added [BugHerd](http://bugherd.com) -* Added [ClickTale](http://clicktale.com) -* Cleaned up extraneous `require`'s in many providers - -0.8.4 - March 5, 2013 ---------------------- -* Added support for strings for the `created` trait -* Added `load-date` for getting the page's load time - -0.8.3 - March 4, 2013 ---------------------- -* Added [Sentry](https://getsentry.com) -* Added initial pageview support to more providers -* Allowed HubSpot to recognize email `userId` -* Added support for DoubleClick [via Google Analytics](http://support.google.com/analytics/bin/answer.py?hl-en&answer-2444872) - -0.8.2 - March 4, 2013 ---------------------- -* Fixed bug in FoxMetrics provider -* Added queue for providers which don't support ready immediately. - -0.8.1 - March 3, 2013 ---------------------- -* Fixed bug in `trackForm` when submitted via jQuery - -0.8.0 - March 1, 2013 ---------------------- -* Added cookie-ing to keep identity and traits across page loads -* Added `identify` support for Clicky -* Added `identify` support for GoSquared -* Added `identify` support for Woopra -* Updated tracking for Usercycle - -0.7.1 - February 26, 2013 -------------------------- -* Added Intercom companies by [@adrianrego](https://github.com/adrianrego) -* Added Intercom setting for use_counter -* Fixed Intercom traits passed without a created field - -0.7.0 - February 25, 2013 -------------------------- -* Switched over to [Component](http://component.io/) - -0.6.0 - February 7, 2013 ------------------------- -* Added `ready` method for binding to when analytics are initialized -* Added [UserVoice](https://www.uservoice.com) -* Added [Perfect Audience](https://www.perfectaudience.com/) -* Added [LiveChat](http://livechatinc.com) -* Fixed Intercom to allow multiple `identify` calls - -0.5.1 - February 4, 2013 ------------------------- -* Merged in fix for Keen IO's branding -* Added fix to `utils.parseUrl()` field `pathname` in IE - -0.5.0 - February 1, 2013 ------------------------- -* Added an `alias` method for merging two user's by ID - -0.4.10 - January 30, 2013 -------------------------- -* Fixed multiple elements on `trackLink` and `trackForm` -* Fixed CrazyEgg `apiKey` to `accountNumber` -* Fixed Keen to Keen.io - - -0.4.9 - January 29, 2013 ------------------------- -* Fixed `alias` and `extend` breaking on non-objects - -0.4.8 - January 29, 2013 ------------------------- -* Fixed `trackForm` timeout by [@Plasma](https://github.com/Plasma) - -0.4.7 - January 29, 2013 ------------------------- -* Added support for Mixpanel's [revenue](https://mixpanel.com/docs/people-analytics/javascript#track_charge) feature -* Added support for KISSmetrics' `"Billing Amount"` property for revenue -* Added support for `revenue` being passed to Google Analytics' `value` property - -0.4.6 - January 28, 2013 ------------------------- -* Added automatic canonical URL support in Google Analytics - -0.4.5 - January 25, 2013 ------------------------- -* Added Intercom widget setting -* Fixed Chartbeat from requiring `body` element to exist - -0.4.4 - January 21, 2013 ------------------------- -* Added [Bitdeli](https://bitdeli.com/) by [@jtuulos](https://github.com/jtuulos) -* Added Mixpanel `$first_name` and `$last_name` aliases by [@dwradcliffe](https://github.com/dwradcliffe) -* Fixed Mixpanel `$last_seen` alias -* Added `parseUrl` util -* Moved GoSquared queue to snippet - -0.4.3 - January 20, 2013 ------------------------- -* Added support for Errorception [user metadata](http://blog.errorception.com/2012/11/capture-custom-data-with-your-errors.html) - -0.4.2 - January 18, 2013 ------------------------- -* Added option to use a properties function in `trackLink` and `trackForm` methods - -0.4.1 - January 18, 2013 ------------------------- -* Added [Keen.io](http://keen.io/) by [@dkador](https://github.com/dkador) -* Added [Foxmetrics](http://foxmetrics.com/) by [@rawsoft](https://github.com/rawsoft) -* Updated Google Analytics to include noninteraction and value added by [@rantav](https://github.com/rantav) -* Moved to expect.js from chai for cross-broser support - -0.4.0 - January 18, 2013 ------------------------- -* Renamed `trackClick` to `trackLink` -* Renamed `trackSubmit` to `trackForm` - -0.3.8 - January 18, 2013 ------------------------- -* Fixed Clicky loading slowly - -0.3.7 - January 17, 2013 ------------------------- -* Added [HitTail](http://hittail.com) -* Added [USERcycle](http://usercycle.com) -* Fixed Travis testing - -0.3.6 - January 14, 2013 ------------------------- -* Added [SnapEngage](http://snapengage.com) - -0.3.5 - January 14, 2013 ------------------------- -* Added `trackClick` and `trackForm` helpers - -0.3.4 - January 13, 2013 ------------------------- -* Upgraded to Mixpanel 2.2 by [@loganfuller](https://github.com/loganfuller) - -0.3.3 - January 11, 2013 ------------------------- -* Added [comScore Direct](http://direct.comscore.com) - -0.3.2 - January 11, 2013 ------------------------- -* Added [Quantcast](http://quantcast.com) -* Fixed breaking issue on Clicky -* Updated Makefile for new providers - - -0.3.1 - January 11, 2013 ------------------------- -* Added `label` and `category` support to Google Analytics - -0.3.0 - January 9, 2013 ------------------------ -* Added [Gauges](http://get.gaug.es/) by [@bdougherty](https://github.com/bdougherty) -* Added [Vero](http://www.getvero.com/) -* Added optional `url` argument to `pageview` method - -0.2.5 - January 8, 2013 ------------------------ -* Added [Errorception](http://errorception.com/) -* Added [Clicky](http://clicky.com/) -* Fixed IE 7 bug reported by [@yefremov](https://github.com/yefremov) - -0.2.4 - January 3, 2013 ------------------------ -* Fixed GoSquared trailing comma by [@cmer](https://github.com/cmer) - -0.2.3 - January 2, 2013 ------------------------ -* Added domain field to GA by [@starrhorne](https://github.com/starrhorne) -* Removed phantom install to get travis working -* Added window._gaq fix in initialize - -0.2.2 - December 19, 2012 -------------------------- -* Added link query tag support for `ajs_uid` and `ajs_event` -* Added docco, uglify, and phantom to `devDependencies` by [@peleteiro](https://github.com/peleteiro) - -0.2.1 - December 18, 2012 -------------------------- -* Added the `pageview` method for tracking virtual pageviews -* Added Travis-CI -* Fixed window level objects in customerio and gosquared -* Added for Intercom's "secure" mode by [@buger](https://github.com/buger) -* Removed root references - -0.2.0 - December 16, 2012 -------------------------- -* Separated providers into separate files for easier maintenance -* Changed special `createdAt` trait to `created` for cleanliness -* Moved `utils` directly onto the analytics object -* Added `extend` and `alias` utils -* Added `settings` defaults for all providers - -0.1.2 - December 14, 2012 -------------------------- -* Fixed bug with HubSpot calls pre-script load -* Upgraded sinon-chai to use [callWithMatch version](https://github.com/obmarg/sinon-chai/blob/f7aa7eccd6c0c18a3e1fc524a246a50c1a29c916/lib/sinon-chai.js) -* Added [Klaviyo](http://www.klaviyo.com/) by [@bialecki](https://github.com/bialecki) -* Added [HubSpot](http://www.hubspot.com/) by [@jessbrandi](https://github.com/jessbrandi) -* Added [GoSquared](https://www.gosquared.com/) by [@simontabor](https://github.com/simontabor) - -0.1.1 - November 25, 2012 -------------------------- -* Added "Enhanced Link Attribution" for Google Analytics by [@nscott](https://github.com/nscott) -* Added "Site Speed Sample Rate" for Google Analytics by [@nscott](https://github.com/nscott) - -0.1.0 - November 11, 2012 -------------------------- -* Added [Olark](http://www.olark.com/) -* Added terse `initialize` syntax -* Added tests for all providers -* Added README diff --git a/Makefile b/Makefile deleted file mode 100644 index d1ee8521f..000000000 --- a/Makefile +++ /dev/null @@ -1,25 +0,0 @@ - -SRC = $(wildcard lib/*.js) - -node_modules: package.json - npm install - touch $@ - -clean: - rm -rf *.log analytics.js analytics.min.js - -distclean: clean - rm -rf components node_modules - -analytics.js: node_modules $(SRC) package.json - ./node_modules/.bin/duo --stdout --standalone analytics lib/index.js > $@ - -analytics.min.js: analytics.js - ./node_modules/.bin/uglifyjs $< --output $@ - -build: analytics.min.js - -lint: node_modules - ./node_modules/.bin/standard - -.PHONY: clean distclean build lint diff --git a/ajs.png b/ajs.png deleted file mode 100644 index e42337686..000000000 Binary files a/ajs.png and /dev/null differ diff --git a/ajs_2x.png b/ajs_2x.png deleted file mode 100644 index fef1370a7..000000000 Binary files a/ajs_2x.png and /dev/null differ diff --git a/analytics.js b/analytics.js deleted file mode 100644 index e2c49d4f0..000000000 --- a/analytics.js +++ /dev/null @@ -1,20420 +0,0 @@ -(function umd(require){ - if (typeof exports === 'object') { - module.exports = require('1'); - } else if (typeof define === 'function' && (define.amd || define.cmd)) { - define(function(){ return require('1'); }); - } else { - this['analytics'] = require('1'); - } -})((function outer(modules, cache, entries){ - - /** - * Global - */ - - var global = (function(){ return this; })(); - - /** - * Require `name`. - * - * @param {String} name - * @api public - */ - - function require(name){ - if (cache[name]) return cache[name].exports; - if (modules[name]) return call(name, require); - throw new Error('cannot find module "' + name + '"'); - } - - /** - * Call module `id` and cache it. - * - * @param {Number} id - * @param {Function} require - * @return {Function} - * @api private - */ - - function call(id, require){ - var m = cache[id] = { exports: {} }; - var mod = modules[id]; - var name = mod[2]; - var fn = mod[0]; - var threw = true; - - try { - fn.call(m.exports, function(req){ - var dep = modules[id][1][req]; - return require(dep || req); - }, m, m.exports, outer, modules, cache, entries); - threw = false; - } finally { - if (threw) { - delete cache[id]; - } else if (name) { - // expose as 'name'. - cache[name] = cache[id]; - } - } - - return cache[id].exports; - } - - /** - * Require all entries exposing them on global if needed. - */ - - for (var id in entries) { - if (entries[id]) { - global[entries[id]] = require(id); - } else { - require(id); - } - } - - /** - * Duo flag. - */ - - require.duo = true; - - /** - * Expose cache. - */ - - require.cache = cache; - - /** - * Expose modules - */ - - require.modules = modules; - - /** - * Return newest require. - */ - - return require; -})({ -1: [function(require, module, exports) { - -/** - * Analytics.js - * - * (C) 2015 Segment.io Inc. - */ - -var analytics = require('segmentio/analytics.js-core'); -var Integrations = require('./integrations'); -var each = require('each'); - -/** - * Expose the `analytics` singleton. - */ - -module.exports = exports = analytics; - -/** - * Expose require. - */ - -analytics.require = require; - -/** - * Expose `VERSION`. - */ - -exports.VERSION = require('../bower.json').version; - -/** - * Add integrations. - */ - -each(Integrations, function(name, Integration) { - analytics.use(Integration); -}); - -}, {"segmentio/analytics.js-core":2,"./integrations":3,"each":4,"../bower.json":5}], -2: [function(require, module, exports) { - -/** - * Analytics.js - * - * (C) 2013 Segment.io Inc. - */ - -var Analytics = require('./analytics'); - -/** - * Expose the `analytics` singleton. - */ - -var analytics = module.exports = exports = new Analytics(); - -/** - * Expose require - */ - -analytics.require = require; - -/** - * Expose `VERSION`. - */ - -exports.VERSION = require('../bower.json').version; - -}, {"./analytics":6,"../bower.json":7}], -6: [function(require, module, exports) { - -/** - * Module dependencies. - */ - -var _analytics = window.analytics; -var Emitter = require('emitter'); -var Facade = require('facade'); -var after = require('after'); -var bind = require('bind'); -var callback = require('callback'); -var clone = require('clone'); -var cookie = require('./cookie'); -var debug = require('debug'); -var defaults = require('defaults'); -var each = require('each'); -var group = require('./group'); -var is = require('is'); -var isMeta = require('is-meta'); -var keys = require('object').keys; -var memory = require('./memory'); -var normalize = require('./normalize'); -var on = require('event').bind; -var pageDefaults = require('./pageDefaults'); -var pick = require('pick'); -var prevent = require('prevent'); -var querystring = require('querystring'); -var size = require('object').length; -var store = require('./store'); -var user = require('./user'); -var Alias = Facade.Alias; -var Group = Facade.Group; -var Identify = Facade.Identify; -var Page = Facade.Page; -var Track = Facade.Track; - -/** - * Expose `Analytics`. - */ - -exports = module.exports = Analytics; - -/** - * Expose storage. - */ - -exports.cookie = cookie; -exports.store = store; -exports.memory = memory; - -/** - * Initialize a new `Analytics` instance. - */ - -function Analytics() { - this._options({}); - this.Integrations = {}; - this._integrations = {}; - this._readied = false; - this._timeout = 300; - // XXX: BACKWARDS COMPATIBILITY - this._user = user; - this.log = debug('analytics.js'); - bind.all(this); - - var self = this; - this.on('initialize', function(settings, options){ - if (options.initialPageview) self.page(); - self._parseQuery(); - }); -} - -/** - * Event Emitter. - */ - -Emitter(Analytics.prototype); - -/** - * Use a `plugin`. - * - * @param {Function} plugin - * @return {Analytics} - */ - -Analytics.prototype.use = function(plugin) { - plugin(this); - return this; -}; - -/** - * Define a new `Integration`. - * - * @param {Function} Integration - * @return {Analytics} - */ - -Analytics.prototype.addIntegration = function(Integration) { - var name = Integration.prototype.name; - if (!name) throw new TypeError('attempted to add an invalid integration'); - this.Integrations[name] = Integration; - return this; -}; - -/** - * Initialize with the given integration `settings` and `options`. - * - * Aliased to `init` for convenience. - * - * @param {Object} [settings={}] - * @param {Object} [options={}] - * @return {Analytics} - */ - -Analytics.prototype.init = Analytics.prototype.initialize = function(settings, options) { - settings = settings || {}; - options = options || {}; - - this._options(options); - this._readied = false; - - // clean unknown integrations from settings - var self = this; - each(settings, function(name) { - var Integration = self.Integrations[name]; - if (!Integration) delete settings[name]; - }); - - // add integrations - each(settings, function(name, opts) { - var Integration = self.Integrations[name]; - var integration = new Integration(clone(opts)); - self.log('initialize %o - %o', name, opts); - self.add(integration); - }); - - var integrations = this._integrations; - - // load user now that options are set - user.load(); - group.load(); - - // make ready callback - var ready = after(size(integrations), function() { - self._readied = true; - self.emit('ready'); - }); - - // initialize integrations, passing ready - each(integrations, function(name, integration) { - if (options.initialPageview && integration.options.initialPageview === false) { - integration.page = after(2, integration.page); - } - - integration.analytics = self; - integration.once('ready', ready); - integration.initialize(); - }); - - // backwards compat with angular plugin. - // TODO: remove - this.initialized = true; - - this.emit('initialize', settings, options); - return this; -}; - -/** - * Set the user's `id`. - * - * @param {Mixed} id - */ - -Analytics.prototype.setAnonymousId = function(id){ - this.user().anonymousId(id); - return this; -}; - -/** - * Add an integration. - * - * @param {Integration} integration - */ - -Analytics.prototype.add = function(integration){ - this._integrations[integration.name] = integration; - return this; -}; - -/** - * Identify a user by optional `id` and `traits`. - * - * @param {string} [id=user.id()] User ID. - * @param {Object} [traits=null] User traits. - * @param {Object} [options=null] - * @param {Function} [fn] - * @return {Analytics} - */ - -Analytics.prototype.identify = function(id, traits, options, fn) { - // Argument reshuffling. - /* eslint-disable no-unused-expressions, no-sequences */ - if (is.fn(options)) fn = options, options = null; - if (is.fn(traits)) fn = traits, options = null, traits = null; - if (is.object(id)) options = traits, traits = id, id = user.id(); - /* eslint-enable no-unused-expressions, no-sequences */ - - // clone traits before we manipulate so we don't do anything uncouth, and take - // from `user` so that we carryover anonymous traits - user.identify(id, traits); - - var msg = this.normalize({ - options: options, - traits: user.traits(), - userId: user.id() - }); - - this._invoke('identify', new Identify(msg)); - - // emit - this.emit('identify', id, traits, options); - this._callback(fn); - return this; -}; - -/** - * Return the current user. - * - * @return {Object} - */ - -Analytics.prototype.user = function() { - return user; -}; - -/** - * Identify a group by optional `id` and `traits`. Or, if no arguments are - * supplied, return the current group. - * - * @param {string} [id=group.id()] Group ID. - * @param {Object} [traits=null] Group traits. - * @param {Object} [options=null] - * @param {Function} [fn] - * @return {Analytics|Object} - */ - -Analytics.prototype.group = function(id, traits, options, fn) { - /* eslint-disable no-unused-expressions, no-sequences */ - if (!arguments.length) return group; - if (is.fn(options)) fn = options, options = null; - if (is.fn(traits)) fn = traits, options = null, traits = null; - if (is.object(id)) options = traits, traits = id, id = group.id(); - /* eslint-enable no-unused-expressions, no-sequences */ - - - // grab from group again to make sure we're taking from the source - group.identify(id, traits); - - var msg = this.normalize({ - options: options, - traits: group.traits(), - groupId: group.id() - }); - - this._invoke('group', new Group(msg)); - - this.emit('group', id, traits, options); - this._callback(fn); - return this; -}; - -/** - * Track an `event` that a user has triggered with optional `properties`. - * - * @param {string} event - * @param {Object} [properties=null] - * @param {Object} [options=null] - * @param {Function} [fn] - * @return {Analytics} - */ - -Analytics.prototype.track = function(event, properties, options, fn) { - // Argument reshuffling. - /* eslint-disable no-unused-expressions, no-sequences */ - if (is.fn(options)) fn = options, options = null; - if (is.fn(properties)) fn = properties, options = null, properties = null; - /* eslint-enable no-unused-expressions, no-sequences */ - - // figure out if the event is archived. - var plan = this.options.plan || {}; - var events = plan.track || {}; - - // normalize - var msg = this.normalize({ - properties: properties, - options: options, - event: event - }); - - // plan. - plan = events[event]; - if (plan) { - this.log('plan %o - %o', event, plan); - if (plan.enabled === false) return this._callback(fn); - defaults(msg.integrations, plan.integrations || {}); - } - - this._invoke('track', new Track(msg)); - - this.emit('track', event, properties, options); - this._callback(fn); - return this; -}; - -/** - * Helper method to track an outbound link that would normally navigate away - * from the page before the analytics calls were sent. - * - * BACKWARDS COMPATIBILITY: aliased to `trackClick`. - * - * @param {Element|Array} links - * @param {string|Function} event - * @param {Object|Function} properties (optional) - * @return {Analytics} - */ - -Analytics.prototype.trackClick = Analytics.prototype.trackLink = function(links, event, properties) { - if (!links) return this; - // always arrays, handles jquery - if (is.element(links)) links = [links]; - - var self = this; - each(links, function(el) { - if (!is.element(el)) throw new TypeError('Must pass HTMLElement to `analytics.trackLink`.'); - on(el, 'click', function(e) { - var ev = is.fn(event) ? event(el) : event; - var props = is.fn(properties) ? properties(el) : properties; - var href = el.getAttribute('href') - || el.getAttributeNS('http://www.w3.org/1999/xlink', 'href') - || el.getAttribute('xlink:href'); - - self.track(ev, props); - - if (href && el.target !== '_blank' && !isMeta(e)) { - prevent(e); - self._callback(function() { - window.location.href = href; - }); - } - }); - }); - - return this; -}; - -/** - * Helper method to track an outbound form that would normally navigate away - * from the page before the analytics calls were sent. - * - * BACKWARDS COMPATIBILITY: aliased to `trackSubmit`. - * - * @param {Element|Array} forms - * @param {string|Function} event - * @param {Object|Function} properties (optional) - * @return {Analytics} - */ - -Analytics.prototype.trackSubmit = Analytics.prototype.trackForm = function(forms, event, properties) { - if (!forms) return this; - // always arrays, handles jquery - if (is.element(forms)) forms = [forms]; - - var self = this; - each(forms, function(el) { - if (!is.element(el)) throw new TypeError('Must pass HTMLElement to `analytics.trackForm`.'); - function handler(e) { - prevent(e); - - var ev = is.fn(event) ? event(el) : event; - var props = is.fn(properties) ? properties(el) : properties; - self.track(ev, props); - - self._callback(function() { - el.submit(); - }); - } - - // Support the events happening through jQuery or Zepto instead of through - // the normal DOM API, because `el.submit` doesn't bubble up events... - var $ = window.jQuery || window.Zepto; - if ($) { - $(el).submit(handler); - } else { - on(el, 'submit', handler); - } - }); - - return this; -}; - -/** - * Trigger a pageview, labeling the current page with an optional `category`, - * `name` and `properties`. - * - * @param {string} [category] - * @param {string} [name] - * @param {Object|string} [properties] (or path) - * @param {Object} [options] - * @param {Function} [fn] - * @return {Analytics} - */ - -Analytics.prototype.page = function(category, name, properties, options, fn) { - // Argument reshuffling. - /* eslint-disable no-unused-expressions, no-sequences */ - if (is.fn(options)) fn = options, options = null; - if (is.fn(properties)) fn = properties, options = properties = null; - if (is.fn(name)) fn = name, options = properties = name = null; - if (is.object(category)) options = name, properties = category, name = category = null; - if (is.object(name)) options = properties, properties = name, name = null; - if (is.string(category) && !is.string(name)) name = category, category = null; - /* eslint-enable no-unused-expressions, no-sequences */ - - properties = clone(properties) || {}; - if (name) properties.name = name; - if (category) properties.category = category; - - // Ensure properties has baseline spec properties. - // TODO: Eventually move these entirely to `options.context.page` - var defs = pageDefaults(); - defaults(properties, defs); - - // Mirror user overrides to `options.context.page` (but exclude custom properties) - // (Any page defaults get applied in `this.normalize` for consistency.) - // Weird, yeah--moving special props to `context.page` will fix this in the long term. - var overrides = pick(keys(defs), properties); - if (!is.empty(overrides)) { - options = options || {}; - options.context = options.context || {}; - options.context.page = overrides; - } - - var msg = this.normalize({ - properties: properties, - category: category, - options: options, - name: name - }); - - this._invoke('page', new Page(msg)); - - this.emit('page', category, name, properties, options); - this._callback(fn); - return this; -}; - -/** - * FIXME: BACKWARDS COMPATIBILITY: convert an old `pageview` to a `page` call. - * - * @param {string} [url] - * @return {Analytics} - * @api private - */ - -Analytics.prototype.pageview = function(url) { - var properties = {}; - if (url) properties.path = url; - this.page(properties); - return this; -}; - -/** - * Merge two previously unassociated user identities. - * - * @param {string} to - * @param {string} from (optional) - * @param {Object} options (optional) - * @param {Function} fn (optional) - * @return {Analytics} - */ - -Analytics.prototype.alias = function(to, from, options, fn) { - // Argument reshuffling. - /* eslint-disable no-unused-expressions, no-sequences */ - if (is.fn(options)) fn = options, options = null; - if (is.fn(from)) fn = from, options = null, from = null; - if (is.object(from)) options = from, from = null; - /* eslint-enable no-unused-expressions, no-sequences */ - - var msg = this.normalize({ - options: options, - previousId: from, - userId: to - }); - - this._invoke('alias', new Alias(msg)); - - this.emit('alias', to, from, options); - this._callback(fn); - return this; -}; - -/** - * Register a `fn` to be fired when all the analytics services are ready. - * - * @param {Function} fn - * @return {Analytics} - */ - -Analytics.prototype.ready = function(fn) { - if (is.fn(fn)) { - if (this._readied) { - callback.async(fn); - } else { - this.once('ready', fn); - } - } - return this; -}; - -/** - * Set the `timeout` (in milliseconds) used for callbacks. - * - * @param {Number} timeout - */ - -Analytics.prototype.timeout = function(timeout) { - this._timeout = timeout; -}; - -/** - * Enable or disable debug. - * - * @param {string|boolean} str - */ - -Analytics.prototype.debug = function(str){ - if (!arguments.length || str) { - debug.enable('analytics:' + (str || '*')); - } else { - debug.disable(); - } -}; - -/** - * Apply options. - * - * @param {Object} options - * @return {Analytics} - * @api private - */ - -Analytics.prototype._options = function(options) { - options = options || {}; - this.options = options; - cookie.options(options.cookie); - store.options(options.localStorage); - user.options(options.user); - group.options(options.group); - return this; -}; - -/** - * Callback a `fn` after our defined timeout period. - * - * @param {Function} fn - * @return {Analytics} - * @api private - */ - -Analytics.prototype._callback = function(fn) { - callback.async(fn, this._timeout); - return this; -}; - -/** - * Call `method` with `facade` on all enabled integrations. - * - * @param {string} method - * @param {Facade} facade - * @return {Analytics} - * @api private - */ - -Analytics.prototype._invoke = function(method, facade) { - this.emit('invoke', facade); - - each(this._integrations, function(name, integration) { - if (!facade.enabled(name)) return; - integration.invoke.call(integration, method, facade); - }); - - return this; -}; - -/** - * Push `args`. - * - * @param {Array} args - * @api private - */ - -Analytics.prototype.push = function(args){ - var method = args.shift(); - if (!this[method]) return; - this[method].apply(this, args); -}; - -/** - * Reset group and user traits and id's. - * - * @api public - */ - -Analytics.prototype.reset = function(){ - this.user().logout(); - this.group().logout(); -}; - -/** - * Parse the query string for callable methods. - * - * @return {Analytics} - * @api private - */ - -Analytics.prototype._parseQuery = function() { - // Identify and track any `ajs_uid` and `ajs_event` parameters in the URL. - var q = querystring.parse(window.location.search); - if (q.ajs_uid) this.identify(q.ajs_uid); - if (q.ajs_event) this.track(q.ajs_event); - if (q.ajs_aid) user.anonymousId(q.ajs_aid); - return this; -}; - -/** - * Normalize the given `msg`. - * - * @param {Object} msg - * @return {Object} - */ - -Analytics.prototype.normalize = function(msg){ - msg = normalize(msg, keys(this._integrations)); - if (msg.anonymousId) user.anonymousId(msg.anonymousId); - msg.anonymousId = user.anonymousId(); - - // Ensure all outgoing requests include page data in their contexts. - msg.context.page = defaults(msg.context.page || {}, pageDefaults()); - - return msg; -}; - -/** - * No conflict support. - */ - -Analytics.prototype.noConflict = function(){ - window.analytics = _analytics; - return this; -}; - - -}, {"emitter":8,"facade":9,"after":10,"bind":11,"callback":12,"clone":13,"./cookie":14,"debug":15,"defaults":16,"each":4,"./group":17,"is":18,"is-meta":19,"object":20,"./memory":21,"./normalize":22,"event":23,"./pageDefaults":24,"pick":25,"prevent":26,"querystring":27,"./store":28,"./user":29}], -8: [function(require, module, exports) { - -/** - * Module dependencies. - */ - -var index = require('indexof'); - -/** - * Expose `Emitter`. - */ - -module.exports = Emitter; - -/** - * Initialize a new `Emitter`. - * - * @api public - */ - -function Emitter(obj) { - if (obj) return mixin(obj); -}; - -/** - * Mixin the emitter properties. - * - * @param {Object} obj - * @return {Object} - * @api private - */ - -function mixin(obj) { - for (var key in Emitter.prototype) { - obj[key] = Emitter.prototype[key]; - } - return obj; -} - -/** - * Listen on the given `event` with `fn`. - * - * @param {String} event - * @param {Function} fn - * @return {Emitter} - * @api public - */ - -Emitter.prototype.on = -Emitter.prototype.addEventListener = function(event, fn){ - this._callbacks = this._callbacks || {}; - (this._callbacks[event] = this._callbacks[event] || []) - .push(fn); - return this; -}; - -/** - * Adds an `event` listener that will be invoked a single - * time then automatically removed. - * - * @param {String} event - * @param {Function} fn - * @return {Emitter} - * @api public - */ - -Emitter.prototype.once = function(event, fn){ - var self = this; - this._callbacks = this._callbacks || {}; - - function on() { - self.off(event, on); - fn.apply(this, arguments); - } - - fn._off = on; - this.on(event, on); - return this; -}; - -/** - * Remove the given callback for `event` or all - * registered callbacks. - * - * @param {String} event - * @param {Function} fn - * @return {Emitter} - * @api public - */ - -Emitter.prototype.off = -Emitter.prototype.removeListener = -Emitter.prototype.removeAllListeners = -Emitter.prototype.removeEventListener = function(event, fn){ - this._callbacks = this._callbacks || {}; - - // all - if (0 == arguments.length) { - this._callbacks = {}; - return this; - } - - // specific event - var callbacks = this._callbacks[event]; - if (!callbacks) return this; - - // remove all handlers - if (1 == arguments.length) { - delete this._callbacks[event]; - return this; - } - - // remove specific handler - var i = index(callbacks, fn._off || fn); - if (~i) callbacks.splice(i, 1); - return this; -}; - -/** - * Emit `event` with the given args. - * - * @param {String} event - * @param {Mixed} ... - * @return {Emitter} - */ - -Emitter.prototype.emit = function(event){ - this._callbacks = this._callbacks || {}; - var args = [].slice.call(arguments, 1) - , callbacks = this._callbacks[event]; - - if (callbacks) { - callbacks = callbacks.slice(0); - for (var i = 0, len = callbacks.length; i < len; ++i) { - callbacks[i].apply(this, args); - } - } - - return this; -}; - -/** - * Return array of callbacks for `event`. - * - * @param {String} event - * @return {Array} - * @api public - */ - -Emitter.prototype.listeners = function(event){ - this._callbacks = this._callbacks || {}; - return this._callbacks[event] || []; -}; - -/** - * Check if this emitter has `event` handlers. - * - * @param {String} event - * @return {Boolean} - * @api public - */ - -Emitter.prototype.hasListeners = function(event){ - return !! this.listeners(event).length; -}; - -}, {"indexof":30}], -30: [function(require, module, exports) { -module.exports = function(arr, obj){ - if (arr.indexOf) return arr.indexOf(obj); - for (var i = 0; i < arr.length; ++i) { - if (arr[i] === obj) return i; - } - return -1; -}; -}, {}], -9: [function(require, module, exports) { - -var Facade = require('./facade'); - -/** - * Expose `Facade` facade. - */ - -module.exports = Facade; - -/** - * Expose specific-method facades. - */ - -Facade.Alias = require('./alias'); -Facade.Group = require('./group'); -Facade.Identify = require('./identify'); -Facade.Track = require('./track'); -Facade.Page = require('./page'); -Facade.Screen = require('./screen'); - -}, {"./facade":31,"./alias":32,"./group":33,"./identify":34,"./track":35,"./page":36,"./screen":37}], -31: [function(require, module, exports) { - -var traverse = require('isodate-traverse'); -var isEnabled = require('./is-enabled'); -var clone = require('./utils').clone; -var type = require('./utils').type; -var address = require('./address'); -var objCase = require('obj-case'); -var newDate = require('new-date'); - -/** - * Expose `Facade`. - */ - -module.exports = Facade; - -/** - * Initialize a new `Facade` with an `obj` of arguments. - * - * @param {Object} obj - */ - -function Facade (obj) { - obj = clone(obj); - if (!obj.hasOwnProperty('timestamp')) obj.timestamp = new Date(); - else obj.timestamp = newDate(obj.timestamp); - traverse(obj); - this.obj = obj; -} - -/** - * Mixin address traits. - */ - -address(Facade.prototype); - -/** - * Return a proxy function for a `field` that will attempt to first use methods, - * and fallback to accessing the underlying object directly. You can specify - * deeply nested fields too like: - * - * this.proxy('options.Librato'); - * - * @param {String} field - */ - -Facade.prototype.proxy = function (field) { - var fields = field.split('.'); - field = fields.shift(); - - // Call a function at the beginning to take advantage of facaded fields - var obj = this[field] || this.field(field); - if (!obj) return obj; - if (typeof obj === 'function') obj = obj.call(this) || {}; - if (fields.length === 0) return transform(obj); - - obj = objCase(obj, fields.join('.')); - return transform(obj); -}; - -/** - * Directly access a specific `field` from the underlying object, returning a - * clone so outsiders don't mess with stuff. - * - * @param {String} field - * @return {Mixed} - */ - -Facade.prototype.field = function (field) { - var obj = this.obj[field]; - return transform(obj); -}; - -/** - * Utility method to always proxy a particular `field`. You can specify deeply - * nested fields too like: - * - * Facade.proxy('options.Librato'); - * - * @param {String} field - * @return {Function} - */ - -Facade.proxy = function (field) { - return function () { - return this.proxy(field); - }; -}; - -/** - * Utility method to directly access a `field`. - * - * @param {String} field - * @return {Function} - */ - -Facade.field = function (field) { - return function () { - return this.field(field); - }; -}; - -/** - * Proxy multiple `path`. - * - * @param {String} path - * @return {Array} - */ - -Facade.multi = function(path){ - return function(){ - var multi = this.proxy(path + 's'); - if ('array' == type(multi)) return multi; - var one = this.proxy(path); - if (one) one = [clone(one)]; - return one || []; - }; -}; - -/** - * Proxy one `path`. - * - * @param {String} path - * @return {Mixed} - */ - -Facade.one = function(path){ - return function(){ - var one = this.proxy(path); - if (one) return one; - var multi = this.proxy(path + 's'); - if ('array' == type(multi)) return multi[0]; - }; -}; - -/** - * Get the basic json object of this facade. - * - * @return {Object} - */ - -Facade.prototype.json = function () { - var ret = clone(this.obj); - if (this.type) ret.type = this.type(); - return ret; -}; - -/** - * Get the options of a call (formerly called "context"). If you pass an - * integration name, it will get the options for that specific integration, or - * undefined if the integration is not enabled. - * - * @param {String} integration (optional) - * @return {Object or Null} - */ - -Facade.prototype.context = -Facade.prototype.options = function (integration) { - var options = clone(this.obj.options || this.obj.context) || {}; - if (!integration) return clone(options); - if (!this.enabled(integration)) return; - var integrations = this.integrations(); - var value = integrations[integration] || objCase(integrations, integration); - if ('boolean' == typeof value) value = {}; - return value || {}; -}; - -/** - * Check whether an integration is enabled. - * - * @param {String} integration - * @return {Boolean} - */ - -Facade.prototype.enabled = function (integration) { - var allEnabled = this.proxy('options.providers.all'); - if (typeof allEnabled !== 'boolean') allEnabled = this.proxy('options.all'); - if (typeof allEnabled !== 'boolean') allEnabled = this.proxy('integrations.all'); - if (typeof allEnabled !== 'boolean') allEnabled = true; - - var enabled = allEnabled && isEnabled(integration); - var options = this.integrations(); - - // If the integration is explicitly enabled or disabled, use that - // First, check options.providers for backwards compatibility - if (options.providers && options.providers.hasOwnProperty(integration)) { - enabled = options.providers[integration]; - } - - // Next, check for the integration's existence in 'options' to enable it. - // If the settings are a boolean, use that, otherwise it should be enabled. - if (options.hasOwnProperty(integration)) { - var settings = options[integration]; - if (typeof settings === 'boolean') { - enabled = settings; - } else { - enabled = true; - } - } - - return enabled ? true : false; -}; - -/** - * Get all `integration` options. - * - * @param {String} integration - * @return {Object} - * @api private - */ - -Facade.prototype.integrations = function(){ - return this.obj.integrations - || this.proxy('options.providers') - || this.options(); -}; - -/** - * Check whether the user is active. - * - * @return {Boolean} - */ - -Facade.prototype.active = function () { - var active = this.proxy('options.active'); - if (active === null || active === undefined) active = true; - return active; -}; - -/** - * Get `sessionId / anonymousId`. - * - * @return {Mixed} - * @api public - */ - -Facade.prototype.sessionId = -Facade.prototype.anonymousId = function(){ - return this.field('anonymousId') - || this.field('sessionId'); -}; - -/** - * Get `groupId` from `context.groupId`. - * - * @return {String} - * @api public - */ - -Facade.prototype.groupId = Facade.proxy('options.groupId'); - -/** - * Get the call's "super properties" which are just traits that have been - * passed in as if from an identify call. - * - * @param {Object} aliases - * @return {Object} - */ - -Facade.prototype.traits = function (aliases) { - var ret = this.proxy('options.traits') || {}; - var id = this.userId(); - aliases = aliases || {}; - - if (id) ret.id = id; - - for (var alias in aliases) { - var value = null == this[alias] - ? this.proxy('options.traits.' + alias) - : this[alias](); - if (null == value) continue; - ret[aliases[alias]] = value; - delete ret[alias]; - } - - return ret; -}; - -/** - * Add a convenient way to get the library name and version - */ - -Facade.prototype.library = function(){ - var library = this.proxy('options.library'); - if (!library) return { name: 'unknown', version: null }; - if (typeof library === 'string') return { name: library, version: null }; - return library; -}; - -/** - * Setup some basic proxies. - */ - -Facade.prototype.userId = Facade.field('userId'); -Facade.prototype.channel = Facade.field('channel'); -Facade.prototype.timestamp = Facade.field('timestamp'); -Facade.prototype.userAgent = Facade.proxy('options.userAgent'); -Facade.prototype.ip = Facade.proxy('options.ip'); - -/** - * Return the cloned and traversed object - * - * @param {Mixed} obj - * @return {Mixed} - */ - -function transform(obj){ - var cloned = clone(obj); - return cloned; -} - -}, {"isodate-traverse":38,"./is-enabled":39,"./utils":40,"./address":41,"obj-case":42,"new-date":43}], -38: [function(require, module, exports) { - -var is = require('is'); -var isodate = require('isodate'); -var each; - -try { - each = require('each'); -} catch (err) { - each = require('each-component'); -} - -/** - * Expose `traverse`. - */ - -module.exports = traverse; - -/** - * Traverse an object or array, and return a clone with all ISO strings parsed - * into Date objects. - * - * @param {Object} obj - * @return {Object} - */ - -function traverse (input, strict) { - if (strict === undefined) strict = true; - - if (is.object(input)) return object(input, strict); - if (is.array(input)) return array(input, strict); - return input; -} - -/** - * Object traverser. - * - * @param {Object} obj - * @param {Boolean} strict - * @return {Object} - */ - -function object (obj, strict) { - each(obj, function (key, val) { - if (isodate.is(val, strict)) { - obj[key] = isodate.parse(val); - } else if (is.object(val) || is.array(val)) { - traverse(val, strict); - } - }); - return obj; -} - -/** - * Array traverser. - * - * @param {Array} arr - * @param {Boolean} strict - * @return {Array} - */ - -function array (arr, strict) { - each(arr, function (val, x) { - if (is.object(val)) { - traverse(val, strict); - } else if (isodate.is(val, strict)) { - arr[x] = isodate.parse(val); - } - }); - return arr; -} - -}, {"is":44,"isodate":45,"each":4}], -44: [function(require, module, exports) { - -var isEmpty = require('is-empty'); - -try { - var typeOf = require('type'); -} catch (e) { - var typeOf = require('component-type'); -} - - -/** - * Types. - */ - -var types = [ - 'arguments', - 'array', - 'boolean', - 'date', - 'element', - 'function', - 'null', - 'number', - 'object', - 'regexp', - 'string', - 'undefined' -]; - - -/** - * Expose type checkers. - * - * @param {Mixed} value - * @return {Boolean} - */ - -for (var i = 0, type; type = types[i]; i++) exports[type] = generate(type); - - -/** - * Add alias for `function` for old browsers. - */ - -exports.fn = exports['function']; - - -/** - * Expose `empty` check. - */ - -exports.empty = isEmpty; - - -/** - * Expose `nan` check. - */ - -exports.nan = function (val) { - return exports.number(val) && val != val; -}; - - -/** - * Generate a type checker. - * - * @param {String} type - * @return {Function} - */ - -function generate (type) { - return function (value) { - return type === typeOf(value); - }; -} -}, {"is-empty":46,"type":47,"component-type":47}], -46: [function(require, module, exports) { - -/** - * Expose `isEmpty`. - */ - -module.exports = isEmpty; - - -/** - * Has. - */ - -var has = Object.prototype.hasOwnProperty; - - -/** - * Test whether a value is "empty". - * - * @param {Mixed} val - * @return {Boolean} - */ - -function isEmpty (val) { - if (null == val) return true; - if ('number' == typeof val) return 0 === val; - if (undefined !== val.length) return 0 === val.length; - for (var key in val) if (has.call(val, key)) return false; - return true; -} -}, {}], -47: [function(require, module, exports) { -/** - * toString ref. - */ - -var toString = Object.prototype.toString; - -/** - * Return the type of `val`. - * - * @param {Mixed} val - * @return {String} - * @api public - */ - -module.exports = function(val){ - switch (toString.call(val)) { - case '[object Date]': return 'date'; - case '[object RegExp]': return 'regexp'; - case '[object Arguments]': return 'arguments'; - case '[object Array]': return 'array'; - case '[object Error]': return 'error'; - } - - if (val === null) return 'null'; - if (val === undefined) return 'undefined'; - if (val !== val) return 'nan'; - if (val && val.nodeType === 1) return 'element'; - - val = val.valueOf - ? val.valueOf() - : Object.prototype.valueOf.apply(val) - - return typeof val; -}; - -}, {}], -45: [function(require, module, exports) { - -/** - * Matcher, slightly modified from: - * - * https://github.com/csnover/js-iso8601/blob/lax/iso8601.js - */ - -var matcher = /^(\d{4})(?:-?(\d{2})(?:-?(\d{2}))?)?(?:([ T])(\d{2}):?(\d{2})(?::?(\d{2})(?:[,\.](\d{1,}))?)?(?:(Z)|([+\-])(\d{2})(?::?(\d{2}))?)?)?$/; - - -/** - * Convert an ISO date string to a date. Fallback to native `Date.parse`. - * - * https://github.com/csnover/js-iso8601/blob/lax/iso8601.js - * - * @param {String} iso - * @return {Date} - */ - -exports.parse = function (iso) { - var numericKeys = [1, 5, 6, 7, 11, 12]; - var arr = matcher.exec(iso); - var offset = 0; - - // fallback to native parsing - if (!arr) return new Date(iso); - - // remove undefined values - for (var i = 0, val; val = numericKeys[i]; i++) { - arr[val] = parseInt(arr[val], 10) || 0; - } - - // allow undefined days and months - arr[2] = parseInt(arr[2], 10) || 1; - arr[3] = parseInt(arr[3], 10) || 1; - - // month is 0-11 - arr[2]--; - - // allow abitrary sub-second precision - arr[8] = arr[8] - ? (arr[8] + '00').substring(0, 3) - : 0; - - // apply timezone if one exists - if (arr[4] == ' ') { - offset = new Date().getTimezoneOffset(); - } else if (arr[9] !== 'Z' && arr[10]) { - offset = arr[11] * 60 + arr[12]; - if ('+' == arr[10]) offset = 0 - offset; - } - - var millis = Date.UTC(arr[1], arr[2], arr[3], arr[5], arr[6] + offset, arr[7], arr[8]); - return new Date(millis); -}; - - -/** - * Checks whether a `string` is an ISO date string. `strict` mode requires that - * the date string at least have a year, month and date. - * - * @param {String} string - * @param {Boolean} strict - * @return {Boolean} - */ - -exports.is = function (string, strict) { - if (strict && false === /^\d{4}-\d{2}-\d{2}/.test(string)) return false; - return matcher.test(string); -}; -}, {}], -4: [function(require, module, exports) { - -/** - * Module dependencies. - */ - -var type = require('type'); - -/** - * HOP reference. - */ - -var has = Object.prototype.hasOwnProperty; - -/** - * Iterate the given `obj` and invoke `fn(val, i)`. - * - * @param {String|Array|Object} obj - * @param {Function} fn - * @api public - */ - -module.exports = function(obj, fn){ - switch (type(obj)) { - case 'array': - return array(obj, fn); - case 'object': - if ('number' == typeof obj.length) return array(obj, fn); - return object(obj, fn); - case 'string': - return string(obj, fn); - } -}; - -/** - * Iterate string chars. - * - * @param {String} obj - * @param {Function} fn - * @api private - */ - -function string(obj, fn) { - for (var i = 0; i < obj.length; ++i) { - fn(obj.charAt(i), i); - } -} - -/** - * Iterate object keys. - * - * @param {Object} obj - * @param {Function} fn - * @api private - */ - -function object(obj, fn) { - for (var key in obj) { - if (has.call(obj, key)) { - fn(key, obj[key]); - } - } -} - -/** - * Iterate array-ish. - * - * @param {Array|Object} obj - * @param {Function} fn - * @api private - */ - -function array(obj, fn) { - for (var i = 0; i < obj.length; ++i) { - fn(obj[i], i); - } -} -}, {"type":47}], -39: [function(require, module, exports) { - -/** - * A few integrations are disabled by default. They must be explicitly - * enabled by setting options[Provider] = true. - */ - -var disabled = { - Salesforce: true -}; - -/** - * Check whether an integration should be enabled by default. - * - * @param {String} integration - * @return {Boolean} - */ - -module.exports = function (integration) { - return ! disabled[integration]; -}; -}, {}], -40: [function(require, module, exports) { - -/** - * TODO: use component symlink, everywhere ? - */ - -try { - exports.inherit = require('inherit'); - exports.clone = require('clone'); - exports.type = require('type'); -} catch (e) { - exports.inherit = require('inherit-component'); - exports.clone = require('clone-component'); - exports.type = require('type-component'); -} - -}, {"inherit":48,"clone":49,"type":47}], -48: [function(require, module, exports) { - -module.exports = function(a, b){ - var fn = function(){}; - fn.prototype = b.prototype; - a.prototype = new fn; - a.prototype.constructor = a; -}; -}, {}], -49: [function(require, module, exports) { -/** - * Module dependencies. - */ - -var type; -try { - type = require('component-type'); -} catch (_) { - type = require('type'); -} - -/** - * Module exports. - */ - -module.exports = clone; - -/** - * Clones objects. - * - * @param {Mixed} any object - * @api public - */ - -function clone(obj){ - switch (type(obj)) { - case 'object': - var copy = {}; - for (var key in obj) { - if (obj.hasOwnProperty(key)) { - copy[key] = clone(obj[key]); - } - } - return copy; - - case 'array': - var copy = new Array(obj.length); - for (var i = 0, l = obj.length; i < l; i++) { - copy[i] = clone(obj[i]); - } - return copy; - - case 'regexp': - // from millermedeiros/amd-utils - MIT - var flags = ''; - flags += obj.multiline ? 'm' : ''; - flags += obj.global ? 'g' : ''; - flags += obj.ignoreCase ? 'i' : ''; - return new RegExp(obj.source, flags); - - case 'date': - return new Date(obj.getTime()); - - default: // string, number, boolean, … - return obj; - } -} - -}, {"component-type":47,"type":47}], -41: [function(require, module, exports) { - -/** - * Module dependencies. - */ - -var get = require('obj-case'); - -/** - * Add address getters to `proto`. - * - * @param {Function} proto - */ - -module.exports = function(proto){ - proto.zip = trait('postalCode', 'zip'); - proto.country = trait('country'); - proto.street = trait('street'); - proto.state = trait('state'); - proto.city = trait('city'); - - function trait(a, b){ - return function(){ - var traits = this.traits(); - var props = this.properties ? this.properties() : {}; - - return get(traits, 'address.' + a) - || get(traits, a) - || (b ? get(traits, 'address.' + b) : null) - || (b ? get(traits, b) : null) - || get(props, 'address.' + a) - || get(props, a) - || (b ? get(props, 'address.' + b) : null) - || (b ? get(props, b) : null); - }; - } -}; - -}, {"obj-case":42}], -42: [function(require, module, exports) { - -var identity = function(_){ return _; }; - - -/** - * Module exports, export - */ - -module.exports = multiple(find); -module.exports.find = module.exports; - - -/** - * Export the replacement function, return the modified object - */ - -module.exports.replace = function (obj, key, val, options) { - multiple(replace).call(this, obj, key, val, options); - return obj; -}; - - -/** - * Export the delete function, return the modified object - */ - -module.exports.del = function (obj, key, options) { - multiple(del).call(this, obj, key, null, options); - return obj; -}; - - -/** - * Compose applying the function to a nested key - */ - -function multiple (fn) { - return function (obj, path, val, options) { - var normalize = options && isFunction(options.normalizer) ? options.normalizer : defaultNormalize; - path = normalize(path); - - var key; - var finished = false; - - while (!finished) loop(); - - function loop() { - for (key in obj) { - var normalizedKey = normalize(key); - if (0 === path.indexOf(normalizedKey)) { - var temp = path.substr(normalizedKey.length); - if (temp.charAt(0) === '.' || temp.length === 0) { - path = temp.substr(1); - var child = obj[key]; - - // we're at the end and there is nothing. - if (null == child) { - finished = true; - return; - } - - // we're at the end and there is something. - if (!path.length) { - finished = true; - return; - } - - // step into child - obj = child; - - // but we're done here - return; - } - } - } - - key = undefined; - // if we found no matching properties - // on the current object, there's no match. - finished = true; - } - - if (!key) return; - if (null == obj) return obj; - - // the `obj` and `key` is one above the leaf object and key, so - // start object: { a: { 'b.c': 10 } } - // end object: { 'b.c': 10 } - // end key: 'b.c' - // this way, you can do `obj[key]` and get `10`. - return fn(obj, key, val); - }; -} - - -/** - * Find an object by its key - * - * find({ first_name : 'Calvin' }, 'firstName') - */ - -function find (obj, key) { - if (obj.hasOwnProperty(key)) return obj[key]; -} - - -/** - * Delete a value for a given key - * - * del({ a : 'b', x : 'y' }, 'X' }) -> { a : 'b' } - */ - -function del (obj, key) { - if (obj.hasOwnProperty(key)) delete obj[key]; - return obj; -} - - -/** - * Replace an objects existing value with a new one - * - * replace({ a : 'b' }, 'a', 'c') -> { a : 'c' } - */ - -function replace (obj, key, val) { - if (obj.hasOwnProperty(key)) obj[key] = val; - return obj; -} - -/** - * Normalize a `dot.separated.path`. - * - * A.HELL(!*&#(!)O_WOR LD.bar => ahelloworldbar - * - * @param {String} path - * @return {String} - */ - -function defaultNormalize(path) { - return path.replace(/[^a-zA-Z0-9\.]+/g, '').toLowerCase(); -} - -/** - * Check if a value is a function. - * - * @param {*} val - * @return {boolean} Returns `true` if `val` is a function, otherwise `false`. - */ - -function isFunction(val) { - return typeof val === 'function'; -} - -}, {}], -43: [function(require, module, exports) { - -var is = require('is'); -var isodate = require('isodate'); -var milliseconds = require('./milliseconds'); -var seconds = require('./seconds'); - - -/** - * Returns a new Javascript Date object, allowing a variety of extra input types - * over the native Date constructor. - * - * @param {Date|String|Number} val - */ - -module.exports = function newDate (val) { - if (is.date(val)) return val; - if (is.number(val)) return new Date(toMs(val)); - - // date strings - if (isodate.is(val)) return isodate.parse(val); - if (milliseconds.is(val)) return milliseconds.parse(val); - if (seconds.is(val)) return seconds.parse(val); - - // fallback to Date.parse - return new Date(val); -}; - - -/** - * If the number passed val is seconds from the epoch, turn it into milliseconds. - * Milliseconds would be greater than 31557600000 (December 31, 1970). - * - * @param {Number} num - */ - -function toMs (num) { - if (num < 31557600000) return num * 1000; - return num; -} -}, {"is":50,"isodate":45,"./milliseconds":51,"./seconds":52}], -50: [function(require, module, exports) { - -var isEmpty = require('is-empty') - , typeOf = require('type'); - - -/** - * Types. - */ - -var types = [ - 'arguments', - 'array', - 'boolean', - 'date', - 'element', - 'function', - 'null', - 'number', - 'object', - 'regexp', - 'string', - 'undefined' -]; - - -/** - * Expose type checkers. - * - * @param {Mixed} value - * @return {Boolean} - */ - -for (var i = 0, type; type = types[i]; i++) exports[type] = generate(type); - - -/** - * Add alias for `function` for old browsers. - */ - -exports.fn = exports['function']; - - -/** - * Expose `empty` check. - */ - -exports.empty = isEmpty; - - -/** - * Expose `nan` check. - */ - -exports.nan = function (val) { - return exports.number(val) && val != val; -}; - - -/** - * Generate a type checker. - * - * @param {String} type - * @return {Function} - */ - -function generate (type) { - return function (value) { - return type === typeOf(value); - }; -} -}, {"is-empty":46,"type":47}], -51: [function(require, module, exports) { - -/** - * Matcher. - */ - -var matcher = /\d{13}/; - - -/** - * Check whether a string is a millisecond date string. - * - * @param {String} string - * @return {Boolean} - */ - -exports.is = function (string) { - return matcher.test(string); -}; - - -/** - * Convert a millisecond string to a date. - * - * @param {String} millis - * @return {Date} - */ - -exports.parse = function (millis) { - millis = parseInt(millis, 10); - return new Date(millis); -}; -}, {}], -52: [function(require, module, exports) { - -/** - * Matcher. - */ - -var matcher = /\d{10}/; - - -/** - * Check whether a string is a second date string. - * - * @param {String} string - * @return {Boolean} - */ - -exports.is = function (string) { - return matcher.test(string); -}; - - -/** - * Convert a second string to a date. - * - * @param {String} seconds - * @return {Date} - */ - -exports.parse = function (seconds) { - var millis = parseInt(seconds, 10) * 1000; - return new Date(millis); -}; -}, {}], -32: [function(require, module, exports) { - -/** - * Module dependencies. - */ - -var inherit = require('./utils').inherit; -var Facade = require('./facade'); - -/** - * Expose `Alias` facade. - */ - -module.exports = Alias; - -/** - * Initialize a new `Alias` facade with a `dictionary` of arguments. - * - * @param {Object} dictionary - * @property {String} from - * @property {String} to - * @property {Object} options - */ - -function Alias (dictionary) { - Facade.call(this, dictionary); -} - -/** - * Inherit from `Facade`. - */ - -inherit(Alias, Facade); - -/** - * Return type of facade. - * - * @return {String} - */ - -Alias.prototype.type = -Alias.prototype.action = function () { - return 'alias'; -}; - -/** - * Get `previousId`. - * - * @return {Mixed} - * @api public - */ - -Alias.prototype.from = -Alias.prototype.previousId = function(){ - return this.field('previousId') - || this.field('from'); -}; - -/** - * Get `userId`. - * - * @return {String} - * @api public - */ - -Alias.prototype.to = -Alias.prototype.userId = function(){ - return this.field('userId') - || this.field('to'); -}; - -}, {"./utils":40,"./facade":31}], -33: [function(require, module, exports) { - -/** - * Module dependencies. - */ - -var inherit = require('./utils').inherit; -var address = require('./address'); -var isEmail = require('is-email'); -var newDate = require('new-date'); -var Facade = require('./facade'); - -/** - * Expose `Group` facade. - */ - -module.exports = Group; - -/** - * Initialize a new `Group` facade with a `dictionary` of arguments. - * - * @param {Object} dictionary - * @param {String} userId - * @param {String} groupId - * @param {Object} properties - * @param {Object} options - */ - -function Group (dictionary) { - Facade.call(this, dictionary); -} - -/** - * Inherit from `Facade` - */ - -inherit(Group, Facade); - -/** - * Get the facade's action. - */ - -Group.prototype.type = -Group.prototype.action = function () { - return 'group'; -}; - -/** - * Setup some basic proxies. - */ - -Group.prototype.groupId = Facade.field('groupId'); - -/** - * Get created or createdAt. - * - * @return {Date} - */ - -Group.prototype.created = function(){ - var created = this.proxy('traits.createdAt') - || this.proxy('traits.created') - || this.proxy('properties.createdAt') - || this.proxy('properties.created'); - - if (created) return newDate(created); -}; - -/** - * Get the group's email, falling back to the group ID if it's a valid email. - * - * @return {String} - */ - -Group.prototype.email = function () { - var email = this.proxy('traits.email'); - if (email) return email; - var groupId = this.groupId(); - if (isEmail(groupId)) return groupId; -}; - -/** - * Get the group's traits. - * - * @param {Object} aliases - * @return {Object} - */ - -Group.prototype.traits = function (aliases) { - var ret = this.properties(); - var id = this.groupId(); - aliases = aliases || {}; - - if (id) ret.id = id; - - for (var alias in aliases) { - var value = null == this[alias] - ? this.proxy('traits.' + alias) - : this[alias](); - if (null == value) continue; - ret[aliases[alias]] = value; - delete ret[alias]; - } - - return ret; -}; - -/** - * Special traits. - */ - -Group.prototype.name = Facade.proxy('traits.name'); -Group.prototype.industry = Facade.proxy('traits.industry'); -Group.prototype.employees = Facade.proxy('traits.employees'); - -/** - * Get traits or properties. - * - * TODO: remove me - * - * @return {Object} - */ - -Group.prototype.properties = function(){ - return this.field('traits') - || this.field('properties') - || {}; -}; - -}, {"./utils":40,"./address":41,"is-email":53,"new-date":43,"./facade":31}], -53: [function(require, module, exports) { - -/** - * Expose `isEmail`. - */ - -module.exports = isEmail; - - -/** - * Email address matcher. - */ - -var matcher = /.+\@.+\..+/; - - -/** - * Loosely validate an email address. - * - * @param {String} string - * @return {Boolean} - */ - -function isEmail (string) { - return matcher.test(string); -} -}, {}], -34: [function(require, module, exports) { - -var address = require('./address'); -var Facade = require('./facade'); -var isEmail = require('is-email'); -var newDate = require('new-date'); -var utils = require('./utils'); -var get = require('obj-case'); -var trim = require('trim'); -var inherit = utils.inherit; -var clone = utils.clone; -var type = utils.type; - -/** - * Expose `Idenfity` facade. - */ - -module.exports = Identify; - -/** - * Initialize a new `Identify` facade with a `dictionary` of arguments. - * - * @param {Object} dictionary - * @param {String} userId - * @param {String} sessionId - * @param {Object} traits - * @param {Object} options - */ - -function Identify (dictionary) { - Facade.call(this, dictionary); -} - -/** - * Inherit from `Facade`. - */ - -inherit(Identify, Facade); - -/** - * Get the facade's action. - */ - -Identify.prototype.type = -Identify.prototype.action = function () { - return 'identify'; -}; - -/** - * Get the user's traits. - * - * @param {Object} aliases - * @return {Object} - */ - -Identify.prototype.traits = function (aliases) { - var ret = this.field('traits') || {}; - var id = this.userId(); - aliases = aliases || {}; - - if (id) ret.id = id; - - for (var alias in aliases) { - var value = null == this[alias] - ? this.proxy('traits.' + alias) - : this[alias](); - if (null == value) continue; - ret[aliases[alias]] = value; - if (alias !== aliases[alias]) delete ret[alias]; - } - - return ret; -}; - -/** - * Get the user's email, falling back to their user ID if it's a valid email. - * - * @return {String} - */ - -Identify.prototype.email = function () { - var email = this.proxy('traits.email'); - if (email) return email; - - var userId = this.userId(); - if (isEmail(userId)) return userId; -}; - -/** - * Get the user's created date, optionally looking for `createdAt` since lots of - * people do that instead. - * - * @return {Date or Undefined} - */ - -Identify.prototype.created = function () { - var created = this.proxy('traits.created') || this.proxy('traits.createdAt'); - if (created) return newDate(created); -}; - -/** - * Get the company created date. - * - * @return {Date or undefined} - */ - -Identify.prototype.companyCreated = function(){ - var created = this.proxy('traits.company.created') - || this.proxy('traits.company.createdAt'); - - if (created) return newDate(created); -}; - -/** - * Get the user's name, optionally combining a first and last name if that's all - * that was provided. - * - * @return {String or Undefined} - */ - -Identify.prototype.name = function () { - var name = this.proxy('traits.name'); - if (typeof name === 'string') return trim(name); - - var firstName = this.firstName(); - var lastName = this.lastName(); - if (firstName && lastName) return trim(firstName + ' ' + lastName); -}; - -/** - * Get the user's first name, optionally splitting it out of a single name if - * that's all that was provided. - * - * @return {String or Undefined} - */ - -Identify.prototype.firstName = function () { - var firstName = this.proxy('traits.firstName'); - if (typeof firstName === 'string') return trim(firstName); - - var name = this.proxy('traits.name'); - if (typeof name === 'string') return trim(name).split(' ')[0]; -}; - -/** - * Get the user's last name, optionally splitting it out of a single name if - * that's all that was provided. - * - * @return {String or Undefined} - */ - -Identify.prototype.lastName = function () { - var lastName = this.proxy('traits.lastName'); - if (typeof lastName === 'string') return trim(lastName); - - var name = this.proxy('traits.name'); - if (typeof name !== 'string') return; - - var space = trim(name).indexOf(' '); - if (space === -1) return; - - return trim(name.substr(space + 1)); -}; - -/** - * Get the user's unique id. - * - * @return {String or undefined} - */ - -Identify.prototype.uid = function(){ - return this.userId() - || this.username() - || this.email(); -}; - -/** - * Get description. - * - * @return {String} - */ - -Identify.prototype.description = function(){ - return this.proxy('traits.description') - || this.proxy('traits.background'); -}; - -/** - * Get the age. - * - * If the age is not explicitly set - * the method will compute it from `.birthday()` - * if possible. - * - * @return {Number} - */ - -Identify.prototype.age = function(){ - var date = this.birthday(); - var age = get(this.traits(), 'age'); - if (null != age) return age; - if ('date' != type(date)) return; - var now = new Date; - return now.getFullYear() - date.getFullYear(); -}; - -/** - * Get the avatar. - * - * .photoUrl needed because help-scout - * implementation uses `.avatar || .photoUrl`. - * - * .avatarUrl needed because trakio uses it. - * - * @return {Mixed} - */ - -Identify.prototype.avatar = function(){ - var traits = this.traits(); - return get(traits, 'avatar') - || get(traits, 'photoUrl') - || get(traits, 'avatarUrl'); -}; - -/** - * Get the position. - * - * .jobTitle needed because some integrations use it. - * - * @return {Mixed} - */ - -Identify.prototype.position = function(){ - var traits = this.traits(); - return get(traits, 'position') || get(traits, 'jobTitle'); -}; - -/** - * Setup sme basic "special" trait proxies. - */ - -Identify.prototype.username = Facade.proxy('traits.username'); -Identify.prototype.website = Facade.one('traits.website'); -Identify.prototype.websites = Facade.multi('traits.website'); -Identify.prototype.phone = Facade.one('traits.phone'); -Identify.prototype.phones = Facade.multi('traits.phone'); -Identify.prototype.address = Facade.proxy('traits.address'); -Identify.prototype.gender = Facade.proxy('traits.gender'); -Identify.prototype.birthday = Facade.proxy('traits.birthday'); - -}, {"./address":41,"./facade":31,"is-email":53,"new-date":43,"./utils":40,"obj-case":42,"trim":54}], -54: [function(require, module, exports) { - -exports = module.exports = trim; - -function trim(str){ - if (str.trim) return str.trim(); - return str.replace(/^\s*|\s*$/g, ''); -} - -exports.left = function(str){ - if (str.trimLeft) return str.trimLeft(); - return str.replace(/^\s*/, ''); -}; - -exports.right = function(str){ - if (str.trimRight) return str.trimRight(); - return str.replace(/\s*$/, ''); -}; - -}, {}], -35: [function(require, module, exports) { - -var inherit = require('./utils').inherit; -var clone = require('./utils').clone; -var type = require('./utils').type; -var Facade = require('./facade'); -var Identify = require('./identify'); -var isEmail = require('is-email'); -var get = require('obj-case'); - -/** - * Expose `Track` facade. - */ - -module.exports = Track; - -/** - * Initialize a new `Track` facade with a `dictionary` of arguments. - * - * @param {object} dictionary - * @property {String} event - * @property {String} userId - * @property {String} sessionId - * @property {Object} properties - * @property {Object} options - */ - -function Track (dictionary) { - Facade.call(this, dictionary); -} - -/** - * Inherit from `Facade`. - */ - -inherit(Track, Facade); - -/** - * Return the facade's action. - * - * @return {String} - */ - -Track.prototype.type = -Track.prototype.action = function () { - return 'track'; -}; - -/** - * Setup some basic proxies. - */ - -Track.prototype.event = Facade.field('event'); -Track.prototype.value = Facade.proxy('properties.value'); - -/** - * Misc - */ - -Track.prototype.category = Facade.proxy('properties.category'); - -/** - * Ecommerce - */ - -Track.prototype.id = Facade.proxy('properties.id'); -Track.prototype.sku = Facade.proxy('properties.sku'); -Track.prototype.tax = Facade.proxy('properties.tax'); -Track.prototype.name = Facade.proxy('properties.name'); -Track.prototype.price = Facade.proxy('properties.price'); -Track.prototype.total = Facade.proxy('properties.total'); -Track.prototype.coupon = Facade.proxy('properties.coupon'); -Track.prototype.shipping = Facade.proxy('properties.shipping'); -Track.prototype.discount = Facade.proxy('properties.discount'); - -/** - * Description - */ - -Track.prototype.description = Facade.proxy('properties.description'); - -/** - * Plan - */ - -Track.prototype.plan = Facade.proxy('properties.plan'); - -/** - * Order id. - * - * @return {String} - * @api public - */ - -Track.prototype.orderId = function(){ - return this.proxy('properties.id') - || this.proxy('properties.orderId'); -}; - -/** - * Get subtotal. - * - * @return {Number} - */ - -Track.prototype.subtotal = function(){ - var subtotal = get(this.properties(), 'subtotal'); - var total = this.total(); - var n; - - if (subtotal) return subtotal; - if (!total) return 0; - if (n = this.tax()) total -= n; - if (n = this.shipping()) total -= n; - if (n = this.discount()) total += n; - - return total; -}; - -/** - * Get products. - * - * @return {Array} - */ - -Track.prototype.products = function(){ - var props = this.properties(); - var products = get(props, 'products'); - return 'array' == type(products) - ? products - : []; -}; - -/** - * Get quantity. - * - * @return {Number} - */ - -Track.prototype.quantity = function(){ - var props = this.obj.properties || {}; - return props.quantity || 1; -}; - -/** - * Get currency. - * - * @return {String} - */ - -Track.prototype.currency = function(){ - var props = this.obj.properties || {}; - return props.currency || 'USD'; -}; - -/** - * BACKWARDS COMPATIBILITY: should probably re-examine where these come from. - */ - -Track.prototype.referrer = Facade.proxy('properties.referrer'); -Track.prototype.query = Facade.proxy('options.query'); - -/** - * Get the call's properties. - * - * @param {Object} aliases - * @return {Object} - */ - -Track.prototype.properties = function (aliases) { - var ret = this.field('properties') || {}; - aliases = aliases || {}; - - for (var alias in aliases) { - var value = null == this[alias] - ? this.proxy('properties.' + alias) - : this[alias](); - if (null == value) continue; - ret[aliases[alias]] = value; - delete ret[alias]; - } - - return ret; -}; - -/** - * Get the call's username. - * - * @return {String or Undefined} - */ - -Track.prototype.username = function () { - return this.proxy('traits.username') || - this.proxy('properties.username') || - this.userId() || - this.sessionId(); -}; - -/** - * Get the call's email, using an the user ID if it's a valid email. - * - * @return {String or Undefined} - */ - -Track.prototype.email = function () { - var email = this.proxy('traits.email'); - email = email || this.proxy('properties.email'); - if (email) return email; - - var userId = this.userId(); - if (isEmail(userId)) return userId; -}; - -/** - * Get the call's revenue, parsing it from a string with an optional leading - * dollar sign. - * - * For products/services that don't have shipping and are not directly taxed, - * they only care about tracking `revenue`. These are things like - * SaaS companies, who sell monthly subscriptions. The subscriptions aren't - * taxed directly, and since it's a digital product, it has no shipping. - * - * The only case where there's a difference between `revenue` and `total` - * (in the context of analytics) is on ecommerce platforms, where they want - * the `revenue` function to actually return the `total` (which includes - * tax and shipping, total = subtotal + tax + shipping). This is probably - * because on their backend they assume tax and shipping has been applied to - * the value, and so can get the revenue on their own. - * - * @return {Number} - */ - -Track.prototype.revenue = function () { - var revenue = this.proxy('properties.revenue'); - var event = this.event(); - - // it's always revenue, unless it's called during an order completion. - if (!revenue && event && event.match(/completed ?order/i)) { - revenue = this.proxy('properties.total'); - } - - return currency(revenue); -}; - -/** - * Get cents. - * - * @return {Number} - */ - -Track.prototype.cents = function(){ - var revenue = this.revenue(); - return 'number' != typeof revenue - ? this.value() || 0 - : revenue * 100; -}; - -/** - * A utility to turn the pieces of a track call into an identify. Used for - * integrations with super properties or rate limits. - * - * TODO: remove me. - * - * @return {Facade} - */ - -Track.prototype.identify = function () { - var json = this.json(); - json.traits = this.traits(); - return new Identify(json); -}; - -/** - * Get float from currency value. - * - * @param {Mixed} val - * @return {Number} - */ - -function currency(val) { - if (!val) return; - if (typeof val === 'number') return val; - if (typeof val !== 'string') return; - - val = val.replace(/\$/g, ''); - val = parseFloat(val); - - if (!isNaN(val)) return val; -} - -}, {"./utils":40,"./facade":31,"./identify":34,"is-email":53,"obj-case":42}], -36: [function(require, module, exports) { - -var inherit = require('./utils').inherit; -var Facade = require('./facade'); -var Track = require('./track'); - -/** - * Expose `Page` facade - */ - -module.exports = Page; - -/** - * Initialize new `Page` facade with `dictionary`. - * - * @param {Object} dictionary - * @param {String} category - * @param {String} name - * @param {Object} traits - * @param {Object} options - */ - -function Page(dictionary){ - Facade.call(this, dictionary); -} - -/** - * Inherit from `Facade` - */ - -inherit(Page, Facade); - -/** - * Get the facade's action. - * - * @return {String} - */ - -Page.prototype.type = -Page.prototype.action = function(){ - return 'page'; -}; - -/** - * Fields - */ - -Page.prototype.category = Facade.field('category'); -Page.prototype.name = Facade.field('name'); - -/** - * Proxies. - */ - -Page.prototype.title = Facade.proxy('properties.title'); -Page.prototype.path = Facade.proxy('properties.path'); -Page.prototype.url = Facade.proxy('properties.url'); - -/** - * Referrer. - */ - -Page.prototype.referrer = function(){ - return this.proxy('properties.referrer') - || this.proxy('context.referrer.url'); -}; - -/** - * Get the page properties mixing `category` and `name`. - * - * @return {Object} - */ - -Page.prototype.properties = function(){ - var props = this.field('properties') || {}; - var category = this.category(); - var name = this.name(); - if (category) props.category = category; - if (name) props.name = name; - return props; -}; - -/** - * Get the page fullName. - * - * @return {String} - */ - -Page.prototype.fullName = function(){ - var category = this.category(); - var name = this.name(); - return name && category - ? category + ' ' + name - : name; -}; - -/** - * Get event with `name`. - * - * @return {String} - */ - -Page.prototype.event = function(name){ - return name - ? 'Viewed ' + name + ' Page' - : 'Loaded a Page'; -}; - -/** - * Convert this Page to a Track facade with `name`. - * - * @param {String} name - * @return {Track} - */ - -Page.prototype.track = function(name){ - var props = this.properties(); - return new Track({ - event: this.event(name), - timestamp: this.timestamp(), - context: this.context(), - properties: props - }); -}; - -}, {"./utils":40,"./facade":31,"./track":35}], -37: [function(require, module, exports) { - -var inherit = require('./utils').inherit; -var Page = require('./page'); -var Track = require('./track'); - -/** - * Expose `Screen` facade - */ - -module.exports = Screen; - -/** - * Initialize new `Screen` facade with `dictionary`. - * - * @param {Object} dictionary - * @param {String} category - * @param {String} name - * @param {Object} traits - * @param {Object} options - */ - -function Screen(dictionary){ - Page.call(this, dictionary); -} - -/** - * Inherit from `Page` - */ - -inherit(Screen, Page); - -/** - * Get the facade's action. - * - * @return {String} - * @api public - */ - -Screen.prototype.type = -Screen.prototype.action = function(){ - return 'screen'; -}; - -/** - * Get event with `name`. - * - * @param {String} name - * @return {String} - * @api public - */ - -Screen.prototype.event = function(name){ - return name - ? 'Viewed ' + name + ' Screen' - : 'Loaded a Screen'; -}; - -/** - * Convert this Screen. - * - * @param {String} name - * @return {Track} - * @api public - */ - -Screen.prototype.track = function(name){ - var props = this.properties(); - return new Track({ - event: this.event(name), - timestamp: this.timestamp(), - context: this.context(), - properties: props - }); -}; - -}, {"./utils":40,"./page":36,"./track":35}], -10: [function(require, module, exports) { - -module.exports = function after (times, func) { - // After 0, really? - if (times <= 0) return func(); - - // That's more like it. - return function() { - if (--times < 1) { - return func.apply(this, arguments); - } - }; -}; -}, {}], -11: [function(require, module, exports) { - -try { - var bind = require('bind'); -} catch (e) { - var bind = require('bind-component'); -} - -var bindAll = require('bind-all'); - - -/** - * Expose `bind`. - */ - -module.exports = exports = bind; - - -/** - * Expose `bindAll`. - */ - -exports.all = bindAll; - - -/** - * Expose `bindMethods`. - */ - -exports.methods = bindMethods; - - -/** - * Bind `methods` on `obj` to always be called with the `obj` as context. - * - * @param {Object} obj - * @param {String} methods... - */ - -function bindMethods (obj, methods) { - methods = [].slice.call(arguments, 1); - for (var i = 0, method; method = methods[i]; i++) { - obj[method] = bind(obj, obj[method]); - } - return obj; -} -}, {"bind":55,"bind-all":56}], -55: [function(require, module, exports) { -/** - * Slice reference. - */ - -var slice = [].slice; - -/** - * Bind `obj` to `fn`. - * - * @param {Object} obj - * @param {Function|String} fn or string - * @return {Function} - * @api public - */ - -module.exports = function(obj, fn){ - if ('string' == typeof fn) fn = obj[fn]; - if ('function' != typeof fn) throw new Error('bind() requires a function'); - var args = slice.call(arguments, 2); - return function(){ - return fn.apply(obj, args.concat(slice.call(arguments))); - } -}; - -}, {}], -56: [function(require, module, exports) { - -try { - var bind = require('bind'); - var type = require('type'); -} catch (e) { - var bind = require('bind-component'); - var type = require('type-component'); -} - -module.exports = function (obj) { - for (var key in obj) { - var val = obj[key]; - if (type(val) === 'function') obj[key] = bind(obj, obj[key]); - } - return obj; -}; -}, {"bind":55,"type":47}], -12: [function(require, module, exports) { -var next = require('next-tick'); - - -/** - * Expose `callback`. - */ - -module.exports = callback; - - -/** - * Call an `fn` back synchronously if it exists. - * - * @param {Function} fn - */ - -function callback (fn) { - if ('function' === typeof fn) fn(); -} - - -/** - * Call an `fn` back asynchronously if it exists. If `wait` is ommitted, the - * `fn` will be called on next tick. - * - * @param {Function} fn - * @param {Number} wait (optional) - */ - -callback.async = function (fn, wait) { - if ('function' !== typeof fn) return; - if (!wait) return next(fn); - setTimeout(fn, wait); -}; - - -/** - * Symmetry. - */ - -callback.sync = callback; - -}, {"next-tick":57}], -57: [function(require, module, exports) { -"use strict" - -if (typeof setImmediate == 'function') { - module.exports = function(f){ setImmediate(f) } -} -// legacy node.js -else if (typeof process != 'undefined' && typeof process.nextTick == 'function') { - module.exports = process.nextTick -} -// fallback for other environments / postMessage behaves badly on IE8 -else if (typeof window == 'undefined' || window.ActiveXObject || !window.postMessage) { - module.exports = function(f){ setTimeout(f) }; -} else { - var q = []; - - window.addEventListener('message', function(){ - var i = 0; - while (i < q.length) { - try { q[i++](); } - catch (e) { - q = q.slice(i); - window.postMessage('tic!', '*'); - throw e; - } - } - q.length = 0; - }, true); - - module.exports = function(fn){ - if (!q.length) window.postMessage('tic!', '*'); - q.push(fn); - } -} - -}, {}], -13: [function(require, module, exports) { - -/** - * Module dependencies. - */ - -var type; - -try { - type = require('type'); -} catch(e){ - type = require('type-component'); -} - -/** - * Module exports. - */ - -module.exports = clone; - -/** - * Clones objects. - * - * @param {Mixed} any object - * @api public - */ - -function clone(obj){ - switch (type(obj)) { - case 'object': - var copy = {}; - for (var key in obj) { - if (obj.hasOwnProperty(key)) { - copy[key] = clone(obj[key]); - } - } - return copy; - - case 'array': - var copy = new Array(obj.length); - for (var i = 0, l = obj.length; i < l; i++) { - copy[i] = clone(obj[i]); - } - return copy; - - case 'regexp': - // from millermedeiros/amd-utils - MIT - var flags = ''; - flags += obj.multiline ? 'm' : ''; - flags += obj.global ? 'g' : ''; - flags += obj.ignoreCase ? 'i' : ''; - return new RegExp(obj.source, flags); - - case 'date': - return new Date(obj.getTime()); - - default: // string, number, boolean, … - return obj; - } -} - -}, {"type":47}], -14: [function(require, module, exports) { - -/** - * Module dependencies. - */ - -var bind = require('bind'); -var clone = require('clone'); -var cookie = require('cookie'); -var debug = require('debug')('analytics.js:cookie'); -var defaults = require('defaults'); -var json = require('json'); -var topDomain = require('top-domain'); - - -/** - * Initialize a new `Cookie` with `options`. - * - * @param {Object} options - */ - -function Cookie(options) { - this.options(options); -} - - -/** - * Get or set the cookie options. - * - * @param {Object} options - * @field {Number} maxage (1 year) - * @field {String} domain - * @field {String} path - * @field {Boolean} secure - */ - -Cookie.prototype.options = function(options) { - if (arguments.length === 0) return this._options; - - options = options || {}; - - var domain = '.' + topDomain(window.location.href); - if (domain === '.') domain = null; - - this._options = defaults(options, { - // default to a year - maxage: 31536000000, - path: '/', - domain: domain - }); - - // http://curl.haxx.se/rfc/cookie_spec.html - // https://publicsuffix.org/list/effective_tld_names.dat - // - // try setting a dummy cookie with the options - // if the cookie isn't set, it probably means - // that the domain is on the public suffix list - // like myapp.herokuapp.com or localhost / ip. - this.set('ajs:test', true); - if (!this.get('ajs:test')) { - debug('fallback to domain=null'); - this._options.domain = null; - } - this.remove('ajs:test'); -}; - - -/** - * Set a `key` and `value` in our cookie. - * - * @param {String} key - * @param {Object} value - * @return {Boolean} saved - */ - -Cookie.prototype.set = function(key, value) { - try { - value = json.stringify(value); - cookie(key, value, clone(this._options)); - return true; - } catch (e) { - return false; - } -}; - - -/** - * Get a value from our cookie by `key`. - * - * @param {String} key - * @return {Object} value - */ - -Cookie.prototype.get = function(key) { - try { - var value = cookie(key); - value = value ? json.parse(value) : null; - return value; - } catch (e) { - return null; - } -}; - - -/** - * Remove a value from our cookie by `key`. - * - * @param {String} key - * @return {Boolean} removed - */ - -Cookie.prototype.remove = function(key) { - try { - cookie(key, null, clone(this._options)); - return true; - } catch (e) { - return false; - } -}; - - -/** - * Expose the cookie singleton. - */ - -module.exports = bind.all(new Cookie()); - - -/** - * Expose the `Cookie` constructor. - */ - -module.exports.Cookie = Cookie; - -}, {"bind":11,"clone":13,"cookie":58,"debug":15,"defaults":16,"json":59,"top-domain":60}], -58: [function(require, module, exports) { - -/** - * Module dependencies. - */ - -var debug = require('debug')('cookie'); - -/** - * Set or get cookie `name` with `value` and `options` object. - * - * @param {String} name - * @param {String} value - * @param {Object} options - * @return {Mixed} - * @api public - */ - -module.exports = function(name, value, options){ - switch (arguments.length) { - case 3: - case 2: - return set(name, value, options); - case 1: - return get(name); - default: - return all(); - } -}; - -/** - * Set cookie `name` to `value`. - * - * @param {String} name - * @param {String} value - * @param {Object} options - * @api private - */ - -function set(name, value, options) { - options = options || {}; - var str = encode(name) + '=' + encode(value); - - if (null == value) options.maxage = -1; - - if (options.maxage) { - options.expires = new Date(+new Date + options.maxage); - } - - if (options.path) str += '; path=' + options.path; - if (options.domain) str += '; domain=' + options.domain; - if (options.expires) str += '; expires=' + options.expires.toUTCString(); - if (options.secure) str += '; secure'; - - document.cookie = str; -} - -/** - * Return all cookies. - * - * @return {Object} - * @api private - */ - -function all() { - return parse(document.cookie); -} - -/** - * Get cookie `name`. - * - * @param {String} name - * @return {String} - * @api private - */ - -function get(name) { - return all()[name]; -} - -/** - * Parse cookie `str`. - * - * @param {String} str - * @return {Object} - * @api private - */ - -function parse(str) { - var obj = {}; - var pairs = str.split(/ *; */); - var pair; - if ('' == pairs[0]) return obj; - for (var i = 0; i < pairs.length; ++i) { - pair = pairs[i].split('='); - obj[decode(pair[0])] = decode(pair[1]); - } - return obj; -} - -/** - * Encode. - */ - -function encode(value){ - try { - return encodeURIComponent(value); - } catch (e) { - debug('error `encode(%o)` - %o', value, e) - } -} - -/** - * Decode. - */ - -function decode(value) { - try { - return decodeURIComponent(value); - } catch (e) { - debug('error `decode(%o)` - %o', value, e) - } -} - -}, {"debug":15}], -15: [function(require, module, exports) { -if ('undefined' == typeof window) { - module.exports = require('./lib/debug'); -} else { - module.exports = require('./debug'); -} - -}, {"./lib/debug":61,"./debug":62}], -61: [function(require, module, exports) { -/** - * Module dependencies. - */ - -var tty = require('tty'); - -/** - * Expose `debug()` as the module. - */ - -module.exports = debug; - -/** - * Enabled debuggers. - */ - -var names = [] - , skips = []; - -(process.env.DEBUG || '') - .split(/[\s,]+/) - .forEach(function(name){ - name = name.replace('*', '.*?'); - if (name[0] === '-') { - skips.push(new RegExp('^' + name.substr(1) + '$')); - } else { - names.push(new RegExp('^' + name + '$')); - } - }); - -/** - * Colors. - */ - -var colors = [6, 2, 3, 4, 5, 1]; - -/** - * Previous debug() call. - */ - -var prev = {}; - -/** - * Previously assigned color. - */ - -var prevColor = 0; - -/** - * Is stdout a TTY? Colored output is disabled when `true`. - */ - -var isatty = tty.isatty(2); - -/** - * Select a color. - * - * @return {Number} - * @api private - */ - -function color() { - return colors[prevColor++ % colors.length]; -} - -/** - * Humanize the given `ms`. - * - * @param {Number} m - * @return {String} - * @api private - */ - -function humanize(ms) { - var sec = 1000 - , min = 60 * 1000 - , hour = 60 * min; - - if (ms >= hour) return (ms / hour).toFixed(1) + 'h'; - if (ms >= min) return (ms / min).toFixed(1) + 'm'; - if (ms >= sec) return (ms / sec | 0) + 's'; - return ms + 'ms'; -} - -/** - * Create a debugger with the given `name`. - * - * @param {String} name - * @return {Type} - * @api public - */ - -function debug(name) { - function disabled(){} - disabled.enabled = false; - - var match = skips.some(function(re){ - return re.test(name); - }); - - if (match) return disabled; - - match = names.some(function(re){ - return re.test(name); - }); - - if (!match) return disabled; - var c = color(); - - function colored(fmt) { - fmt = coerce(fmt); - - var curr = new Date; - var ms = curr - (prev[name] || curr); - prev[name] = curr; - - fmt = ' \u001b[9' + c + 'm' + name + ' ' - + '\u001b[3' + c + 'm\u001b[90m' - + fmt + '\u001b[3' + c + 'm' - + ' +' + humanize(ms) + '\u001b[0m'; - - console.error.apply(this, arguments); - } - - function plain(fmt) { - fmt = coerce(fmt); - - fmt = new Date().toUTCString() - + ' ' + name + ' ' + fmt; - console.error.apply(this, arguments); - } - - colored.enabled = plain.enabled = true; - - return isatty || process.env.DEBUG_COLORS - ? colored - : plain; -} - -/** - * Coerce `val`. - */ - -function coerce(val) { - if (val instanceof Error) return val.stack || val.message; - return val; -} - -}, {}], -62: [function(require, module, exports) { - -/** - * Expose `debug()` as the module. - */ - -module.exports = debug; - -/** - * Create a debugger with the given `name`. - * - * @param {String} name - * @return {Type} - * @api public - */ - -function debug(name) { - if (!debug.enabled(name)) return function(){}; - - return function(fmt){ - fmt = coerce(fmt); - - var curr = new Date; - var ms = curr - (debug[name] || curr); - debug[name] = curr; - - fmt = name - + ' ' - + fmt - + ' +' + debug.humanize(ms); - - // This hackery is required for IE8 - // where `console.log` doesn't have 'apply' - window.console - && console.log - && Function.prototype.apply.call(console.log, console, arguments); - } -} - -/** - * The currently active debug mode names. - */ - -debug.names = []; -debug.skips = []; - -/** - * Enables a debug mode by name. This can include modes - * separated by a colon and wildcards. - * - * @param {String} name - * @api public - */ - -debug.enable = function(name) { - try { - localStorage.debug = name; - } catch(e){} - - var split = (name || '').split(/[\s,]+/) - , len = split.length; - - for (var i = 0; i < len; i++) { - name = split[i].replace('*', '.*?'); - if (name[0] === '-') { - debug.skips.push(new RegExp('^' + name.substr(1) + '$')); - } - else { - debug.names.push(new RegExp('^' + name + '$')); - } - } -}; - -/** - * Disable debug output. - * - * @api public - */ - -debug.disable = function(){ - debug.enable(''); -}; - -/** - * Humanize the given `ms`. - * - * @param {Number} m - * @return {String} - * @api private - */ - -debug.humanize = function(ms) { - var sec = 1000 - , min = 60 * 1000 - , hour = 60 * min; - - if (ms >= hour) return (ms / hour).toFixed(1) + 'h'; - if (ms >= min) return (ms / min).toFixed(1) + 'm'; - if (ms >= sec) return (ms / sec | 0) + 's'; - return ms + 'ms'; -}; - -/** - * Returns true if the given mode name is enabled, false otherwise. - * - * @param {String} name - * @return {Boolean} - * @api public - */ - -debug.enabled = function(name) { - for (var i = 0, len = debug.skips.length; i < len; i++) { - if (debug.skips[i].test(name)) { - return false; - } - } - for (var i = 0, len = debug.names.length; i < len; i++) { - if (debug.names[i].test(name)) { - return true; - } - } - return false; -}; - -/** - * Coerce `val`. - */ - -function coerce(val) { - if (val instanceof Error) return val.stack || val.message; - return val; -} - -// persist - -try { - if (window.localStorage) debug.enable(localStorage.debug); -} catch(e){} - -}, {}], -16: [function(require, module, exports) { -'use strict'; - -/** - * Merge default values. - * - * @param {Object} dest - * @param {Object} defaults - * @return {Object} - * @api public - */ -var defaults = function (dest, src, recursive) { - for (var prop in src) { - if (recursive && dest[prop] instanceof Object && src[prop] instanceof Object) { - dest[prop] = defaults(dest[prop], src[prop], true); - } else if (! (prop in dest)) { - dest[prop] = src[prop]; - } - } - - return dest; -}; - -/** - * Expose `defaults`. - */ -module.exports = defaults; - -}, {}], -59: [function(require, module, exports) { - -var json = window.JSON || {}; -var stringify = json.stringify; -var parse = json.parse; - -module.exports = parse && stringify - ? JSON - : require('json-fallback'); - -}, {"json-fallback":63}], -63: [function(require, module, exports) { -/* - json2.js - 2014-02-04 - - Public Domain. - - NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. - - See http://www.JSON.org/js.html - - - This code should be minified before deployment. - See http://javascript.crockford.com/jsmin.html - - USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO - NOT CONTROL. - - - This file creates a global JSON object containing two methods: stringify - and parse. - - JSON.stringify(value, replacer, space) - value any JavaScript value, usually an object or array. - - replacer an optional parameter that determines how object - values are stringified for objects. It can be a - function or an array of strings. - - space an optional parameter that specifies the indentation - of nested structures. If it is omitted, the text will - be packed without extra whitespace. If it is a number, - it will specify the number of spaces to indent at each - level. If it is a string (such as '\t' or ' '), - it contains the characters used to indent at each level. - - This method produces a JSON text from a JavaScript value. - - When an object value is found, if the object contains a toJSON - method, its toJSON method will be called and the result will be - stringified. A toJSON method does not serialize: it returns the - value represented by the name/value pair that should be serialized, - or undefined if nothing should be serialized. The toJSON method - will be passed the key associated with the value, and this will be - bound to the value - - For example, this would serialize Dates as ISO strings. - - Date.prototype.toJSON = function (key) { - function f(n) { - // Format integers to have at least two digits. - return n < 10 ? '0' + n : n; - } - - return this.getUTCFullYear() + '-' + - f(this.getUTCMonth() + 1) + '-' + - f(this.getUTCDate()) + 'T' + - f(this.getUTCHours()) + ':' + - f(this.getUTCMinutes()) + ':' + - f(this.getUTCSeconds()) + 'Z'; - }; - - You can provide an optional replacer method. It will be passed the - key and value of each member, with this bound to the containing - object. The value that is returned from your method will be - serialized. If your method returns undefined, then the member will - be excluded from the serialization. - - If the replacer parameter is an array of strings, then it will be - used to select the members to be serialized. It filters the results - such that only members with keys listed in the replacer array are - stringified. - - Values that do not have JSON representations, such as undefined or - functions, will not be serialized. Such values in objects will be - dropped; in arrays they will be replaced with null. You can use - a replacer function to replace those with JSON values. - JSON.stringify(undefined) returns undefined. - - The optional space parameter produces a stringification of the - value that is filled with line breaks and indentation to make it - easier to read. - - If the space parameter is a non-empty string, then that string will - be used for indentation. If the space parameter is a number, then - the indentation will be that many spaces. - - Example: - - text = JSON.stringify(['e', {pluribus: 'unum'}]); - // text is '["e",{"pluribus":"unum"}]' - - - text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); - // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' - - text = JSON.stringify([new Date()], function (key, value) { - return this[key] instanceof Date ? - 'Date(' + this[key] + ')' : value; - }); - // text is '["Date(---current time---)"]' - - - JSON.parse(text, reviver) - This method parses a JSON text to produce an object or array. - It can throw a SyntaxError exception. - - The optional reviver parameter is a function that can filter and - transform the results. It receives each of the keys and values, - and its return value is used instead of the original value. - If it returns what it received, then the structure is not modified. - If it returns undefined then the member is deleted. - - Example: - - // Parse the text. Values that look like ISO date strings will - // be converted to Date objects. - - myData = JSON.parse(text, function (key, value) { - var a; - if (typeof value === 'string') { - a = -/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); - if (a) { - return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], - +a[5], +a[6])); - } - } - return value; - }); - - myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { - var d; - if (typeof value === 'string' && - value.slice(0, 5) === 'Date(' && - value.slice(-1) === ')') { - d = new Date(value.slice(5, -1)); - if (d) { - return d; - } - } - return value; - }); - - - This is a reference implementation. You are free to copy, modify, or - redistribute. -*/ - -/*jslint evil: true, regexp: true */ - -/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply, - call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, - getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, - lastIndex, length, parse, prototype, push, replace, slice, stringify, - test, toJSON, toString, valueOf -*/ - - -// Create a JSON object only if one does not already exist. We create the -// methods in a closure to avoid creating global variables. - -(function () { - 'use strict'; - - var JSON = module.exports = {}; - - function f(n) { - // Format integers to have at least two digits. - return n < 10 ? '0' + n : n; - } - - if (typeof Date.prototype.toJSON !== 'function') { - - Date.prototype.toJSON = function () { - - return isFinite(this.valueOf()) - ? this.getUTCFullYear() + '-' + - f(this.getUTCMonth() + 1) + '-' + - f(this.getUTCDate()) + 'T' + - f(this.getUTCHours()) + ':' + - f(this.getUTCMinutes()) + ':' + - f(this.getUTCSeconds()) + 'Z' - : null; - }; - - String.prototype.toJSON = - Number.prototype.toJSON = - Boolean.prototype.toJSON = function () { - return this.valueOf(); - }; - } - - var cx, - escapable, - gap, - indent, - meta, - rep; - - - function quote(string) { - -// If the string contains no control characters, no quote characters, and no -// backslash characters, then we can safely slap some quotes around it. -// Otherwise we must also replace the offending characters with safe escape -// sequences. - - escapable.lastIndex = 0; - return escapable.test(string) ? '"' + string.replace(escapable, function (a) { - var c = meta[a]; - return typeof c === 'string' - ? c - : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); - }) + '"' : '"' + string + '"'; - } - - - function str(key, holder) { - -// Produce a string from holder[key]. - - var i, // The loop counter. - k, // The member key. - v, // The member value. - length, - mind = gap, - partial, - value = holder[key]; - -// If the value has a toJSON method, call it to obtain a replacement value. - - if (value && typeof value === 'object' && - typeof value.toJSON === 'function') { - value = value.toJSON(key); - } - -// If we were called with a replacer function, then call the replacer to -// obtain a replacement value. - - if (typeof rep === 'function') { - value = rep.call(holder, key, value); - } - -// What happens next depends on the value's type. - - switch (typeof value) { - case 'string': - return quote(value); - - case 'number': - -// JSON numbers must be finite. Encode non-finite numbers as null. - - return isFinite(value) ? String(value) : 'null'; - - case 'boolean': - case 'null': - -// If the value is a boolean or null, convert it to a string. Note: -// typeof null does not produce 'null'. The case is included here in -// the remote chance that this gets fixed someday. - - return String(value); - -// If the type is 'object', we might be dealing with an object or an array or -// null. - - case 'object': - -// Due to a specification blunder in ECMAScript, typeof null is 'object', -// so watch out for that case. - - if (!value) { - return 'null'; - } - -// Make an array to hold the partial results of stringifying this object value. - - gap += indent; - partial = []; - -// Is the value an array? - - if (Object.prototype.toString.apply(value) === '[object Array]') { - -// The value is an array. Stringify every element. Use null as a placeholder -// for non-JSON values. - - length = value.length; - for (i = 0; i < length; i += 1) { - partial[i] = str(i, value) || 'null'; - } - -// Join all of the elements together, separated with commas, and wrap them in -// brackets. - - v = partial.length === 0 - ? '[]' - : gap - ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' - : '[' + partial.join(',') + ']'; - gap = mind; - return v; - } - -// If the replacer is an array, use it to select the members to be stringified. - - if (rep && typeof rep === 'object') { - length = rep.length; - for (i = 0; i < length; i += 1) { - if (typeof rep[i] === 'string') { - k = rep[i]; - v = str(k, value); - if (v) { - partial.push(quote(k) + (gap ? ': ' : ':') + v); - } - } - } - } else { - -// Otherwise, iterate through all of the keys in the object. - - for (k in value) { - if (Object.prototype.hasOwnProperty.call(value, k)) { - v = str(k, value); - if (v) { - partial.push(quote(k) + (gap ? ': ' : ':') + v); - } - } - } - } - -// Join all of the member texts together, separated with commas, -// and wrap them in braces. - - v = partial.length === 0 - ? '{}' - : gap - ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' - : '{' + partial.join(',') + '}'; - gap = mind; - return v; - } - } - -// If the JSON object does not yet have a stringify method, give it one. - - if (typeof JSON.stringify !== 'function') { - escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; - meta = { // table of character substitutions - '\b': '\\b', - '\t': '\\t', - '\n': '\\n', - '\f': '\\f', - '\r': '\\r', - '"' : '\\"', - '\\': '\\\\' - }; - JSON.stringify = function (value, replacer, space) { - -// The stringify method takes a value and an optional replacer, and an optional -// space parameter, and returns a JSON text. The replacer can be a function -// that can replace values, or an array of strings that will select the keys. -// A default replacer method can be provided. Use of the space parameter can -// produce text that is more easily readable. - - var i; - gap = ''; - indent = ''; - -// If the space parameter is a number, make an indent string containing that -// many spaces. - - if (typeof space === 'number') { - for (i = 0; i < space; i += 1) { - indent += ' '; - } - -// If the space parameter is a string, it will be used as the indent string. - - } else if (typeof space === 'string') { - indent = space; - } - -// If there is a replacer, it must be a function or an array. -// Otherwise, throw an error. - - rep = replacer; - if (replacer && typeof replacer !== 'function' && - (typeof replacer !== 'object' || - typeof replacer.length !== 'number')) { - throw new Error('JSON.stringify'); - } - -// Make a fake root object containing our value under the key of ''. -// Return the result of stringifying the value. - - return str('', {'': value}); - }; - } - - -// If the JSON object does not yet have a parse method, give it one. - - if (typeof JSON.parse !== 'function') { - cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; - JSON.parse = function (text, reviver) { - -// The parse method takes a text and an optional reviver function, and returns -// a JavaScript value if the text is a valid JSON text. - - var j; - - function walk(holder, key) { - -// The walk method is used to recursively walk the resulting structure so -// that modifications can be made. - - var k, v, value = holder[key]; - if (value && typeof value === 'object') { - for (k in value) { - if (Object.prototype.hasOwnProperty.call(value, k)) { - v = walk(value, k); - if (v !== undefined) { - value[k] = v; - } else { - delete value[k]; - } - } - } - } - return reviver.call(holder, key, value); - } - - -// Parsing happens in four stages. In the first stage, we replace certain -// Unicode characters with escape sequences. JavaScript handles many characters -// incorrectly, either silently deleting them, or treating them as line endings. - - text = String(text); - cx.lastIndex = 0; - if (cx.test(text)) { - text = text.replace(cx, function (a) { - return '\\u' + - ('0000' + a.charCodeAt(0).toString(16)).slice(-4); - }); - } - -// In the second stage, we run the text against regular expressions that look -// for non-JSON patterns. We are especially concerned with '()' and 'new' -// because they can cause invocation, and '=' because it can cause mutation. -// But just to be safe, we want to reject all unexpected forms. - -// We split the second stage into 4 regexp operations in order to work around -// crippling inefficiencies in IE's and Safari's regexp engines. First we -// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we -// replace all simple value tokens with ']' characters. Third, we delete all -// open brackets that follow a colon or comma or that begin the text. Finally, -// we look to see that the remaining characters are only whitespace or ']' or -// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. - - if (/^[\],:{}\s]*$/ - .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') - .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']') - .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { - -// In the third stage we use the eval function to compile the text into a -// JavaScript structure. The '{' operator is subject to a syntactic ambiguity -// in JavaScript: it can begin a block or an object literal. We wrap the text -// in parens to eliminate the ambiguity. - - j = eval('(' + text + ')'); - -// In the optional fourth stage, we recursively walk the new structure, passing -// each name/value pair to a reviver function for possible transformation. - - return typeof reviver === 'function' - ? walk({'': j}, '') - : j; - } - -// If the text is not JSON parseable, then a SyntaxError is thrown. - - throw new SyntaxError('JSON.parse'); - }; - } -}()); - -}, {}], -60: [function(require, module, exports) { - -/** - * Module dependencies. - */ - -var parse = require('url').parse; -var cookie = require('cookie'); - -/** - * Expose `domain` - */ - -exports = module.exports = domain; - -/** - * Expose `cookie` for testing. - */ - -exports.cookie = cookie; - -/** - * Get the top domain. - * - * The function constructs the levels of domain - * and attempts to set a global cookie on each one - * when it succeeds it returns the top level domain. - * - * The method returns an empty string when the hostname - * is an ip or `localhost`. - * - * Example levels: - * - * domain.levels('http://www.google.co.uk'); - * // => ["co.uk", "google.co.uk", "www.google.co.uk"] - * - * Example: - * - * domain('http://localhost:3000/baz'); - * // => '' - * domain('http://dev:3000/baz'); - * // => '' - * domain('http://127.0.0.1:3000/baz'); - * // => '' - * domain('http://segment.io/baz'); - * // => 'segment.io' - * - * @param {String} url - * @return {String} - * @api public - */ - -function domain(url){ - var cookie = exports.cookie; - var levels = exports.levels(url); - - // Lookup the real top level one. - for (var i = 0; i < levels.length; ++i) { - var cname = '__tld__'; - var domain = levels[i]; - var opts = { domain: '.' + domain }; - - cookie(cname, 1, opts); - if (cookie(cname)) { - cookie(cname, null, opts); - return domain - } - } - - return ''; -}; - -/** - * Levels returns all levels of the given url. - * - * @param {String} url - * @return {Array} - * @api public - */ - -domain.levels = function(url){ - var host = parse(url).hostname; - var parts = host.split('.'); - var last = parts[parts.length-1]; - var levels = []; - - // Ip address. - if (4 == parts.length && parseInt(last, 10) == last) { - return levels; - } - - // Localhost. - if (1 >= parts.length) { - return levels; - } - - // Create levels. - for (var i = parts.length-2; 0 <= i; --i) { - levels.push(parts.slice(i).join('.')); - } - - return levels; -}; - -}, {"url":64,"cookie":65}], -64: [function(require, module, exports) { - -/** - * Parse the given `url`. - * - * @param {String} str - * @return {Object} - * @api public - */ - -exports.parse = function(url){ - var a = document.createElement('a'); - a.href = url; - return { - href: a.href, - host: a.host || location.host, - port: ('0' === a.port || '' === a.port) ? port(a.protocol) : a.port, - hash: a.hash, - hostname: a.hostname || location.hostname, - pathname: a.pathname.charAt(0) != '/' ? '/' + a.pathname : a.pathname, - protocol: !a.protocol || ':' == a.protocol ? location.protocol : a.protocol, - search: a.search, - query: a.search.slice(1) - }; -}; - -/** - * Check if `url` is absolute. - * - * @param {String} url - * @return {Boolean} - * @api public - */ - -exports.isAbsolute = function(url){ - return 0 == url.indexOf('//') || !!~url.indexOf('://'); -}; - -/** - * Check if `url` is relative. - * - * @param {String} url - * @return {Boolean} - * @api public - */ - -exports.isRelative = function(url){ - return !exports.isAbsolute(url); -}; - -/** - * Check if `url` is cross domain. - * - * @param {String} url - * @return {Boolean} - * @api public - */ - -exports.isCrossDomain = function(url){ - url = exports.parse(url); - var location = exports.parse(window.location.href); - return url.hostname !== location.hostname - || url.port !== location.port - || url.protocol !== location.protocol; -}; - -/** - * Return default port for `protocol`. - * - * @param {String} protocol - * @return {String} - * @api private - */ -function port (protocol){ - switch (protocol) { - case 'http:': - return 80; - case 'https:': - return 443; - default: - return location.port; - } -} - -}, {}], -65: [function(require, module, exports) { - -/** - * Module dependencies. - */ - -var debug = require('debug')('cookie'); - -/** - * Set or get cookie `name` with `value` and `options` object. - * - * @param {String} name - * @param {String} value - * @param {Object} options - * @return {Mixed} - * @api public - */ - -module.exports = function(name, value, options){ - switch (arguments.length) { - case 3: - case 2: - return set(name, value, options); - case 1: - return get(name); - default: - return all(); - } -}; - -/** - * Set cookie `name` to `value`. - * - * @param {String} name - * @param {String} value - * @param {Object} options - * @api private - */ - -function set(name, value, options) { - options = options || {}; - var str = encode(name) + '=' + encode(value); - - if (null == value) options.maxage = -1; - - if (options.maxage) { - options.expires = new Date(+new Date + options.maxage); - } - - if (options.path) str += '; path=' + options.path; - if (options.domain) str += '; domain=' + options.domain; - if (options.expires) str += '; expires=' + options.expires.toUTCString(); - if (options.secure) str += '; secure'; - - document.cookie = str; -} - -/** - * Return all cookies. - * - * @return {Object} - * @api private - */ - -function all() { - var str; - try { - str = document.cookie; - } catch (err) { - if (typeof console !== 'undefined' && typeof console.error === 'function') { - console.error(err.stack || err); - } - return {}; - } - return parse(str); -} - -/** - * Get cookie `name`. - * - * @param {String} name - * @return {String} - * @api private - */ - -function get(name) { - return all()[name]; -} - -/** - * Parse cookie `str`. - * - * @param {String} str - * @return {Object} - * @api private - */ - -function parse(str) { - var obj = {}; - var pairs = str.split(/ *; */); - var pair; - if ('' == pairs[0]) return obj; - for (var i = 0; i < pairs.length; ++i) { - pair = pairs[i].split('='); - obj[decode(pair[0])] = decode(pair[1]); - } - return obj; -} - -/** - * Encode. - */ - -function encode(value){ - try { - return encodeURIComponent(value); - } catch (e) { - debug('error `encode(%o)` - %o', value, e) - } -} - -/** - * Decode. - */ - -function decode(value) { - try { - return decodeURIComponent(value); - } catch (e) { - debug('error `decode(%o)` - %o', value, e) - } -} - -}, {"debug":15}], -17: [function(require, module, exports) { - -/** - * Module dependencies. - */ - -var Entity = require('./entity'); -var bind = require('bind'); -var debug = require('debug')('analytics:group'); -var inherit = require('inherit'); - -/** - * Group defaults - */ - -Group.defaults = { - persist: true, - cookie: { - key: 'ajs_group_id' - }, - localStorage: { - key: 'ajs_group_properties' - } -}; - - -/** - * Initialize a new `Group` with `options`. - * - * @param {Object} options - */ - -function Group(options) { - this.defaults = Group.defaults; - this.debug = debug; - Entity.call(this, options); -} - - -/** - * Inherit `Entity` - */ - -inherit(Group, Entity); - - -/** - * Expose the group singleton. - */ - -module.exports = bind.all(new Group()); - - -/** - * Expose the `Group` constructor. - */ - -module.exports.Group = Group; - -}, {"./entity":66,"bind":11,"debug":15,"inherit":67}], -66: [function(require, module, exports) { - -var clone = require('clone'); -var cookie = require('./cookie'); -var debug = require('debug')('analytics:entity'); -var defaults = require('defaults'); -var extend = require('extend'); -var memory = require('./memory'); -var store = require('./store'); -var isodateTraverse = require('isodate-traverse'); - - -/** - * Expose `Entity` - */ - -module.exports = Entity; - - -/** - * Initialize new `Entity` with `options`. - * - * @param {Object} options - */ - -function Entity(options) { - this.options(options); - this.initialize(); -} - -/** - * Initialize picks the storage. - * - * Checks to see if cookies can be set - * otherwise fallsback to localStorage. - */ - -Entity.prototype.initialize = function() { - cookie.set('ajs:cookies', true); - - // cookies are enabled. - if (cookie.get('ajs:cookies')) { - cookie.remove('ajs:cookies'); - this._storage = cookie; - return; - } - - // localStorage is enabled. - if (store.enabled) { - this._storage = store; - return; - } - - // fallback to memory storage. - debug('warning using memory store both cookies and localStorage are disabled'); - this._storage = memory; -}; - -/** - * Get the storage. - */ - -Entity.prototype.storage = function() { - return this._storage; -}; - - -/** - * Get or set storage `options`. - * - * @param {Object} options - * @property {Object} cookie - * @property {Object} localStorage - * @property {Boolean} persist (default: `true`) - */ - -Entity.prototype.options = function(options) { - if (arguments.length === 0) return this._options; - this._options = defaults(options || {}, this.defaults || {}); -}; - - -/** - * Get or set the entity's `id`. - * - * @param {String} id - */ - -Entity.prototype.id = function(id) { - switch (arguments.length) { - case 0: return this._getId(); - case 1: return this._setId(id); - default: - // No default case - } -}; - - -/** - * Get the entity's id. - * - * @return {String} - */ - -Entity.prototype._getId = function() { - var ret = this._options.persist - ? this.storage().get(this._options.cookie.key) - : this._id; - return ret === undefined ? null : ret; -}; - - -/** - * Set the entity's `id`. - * - * @param {String} id - */ - -Entity.prototype._setId = function(id) { - if (this._options.persist) { - this.storage().set(this._options.cookie.key, id); - } else { - this._id = id; - } -}; - - -/** - * Get or set the entity's `traits`. - * - * BACKWARDS COMPATIBILITY: aliased to `properties` - * - * @param {Object} traits - */ - -Entity.prototype.properties = Entity.prototype.traits = function(traits) { - switch (arguments.length) { - case 0: return this._getTraits(); - case 1: return this._setTraits(traits); - default: - // No default case - } -}; - - -/** - * Get the entity's traits. Always convert ISO date strings into real dates, - * since they aren't parsed back from local storage. - * - * @return {Object} - */ - -Entity.prototype._getTraits = function() { - var ret = this._options.persist ? store.get(this._options.localStorage.key) : this._traits; - return ret ? isodateTraverse(clone(ret)) : {}; -}; - - -/** - * Set the entity's `traits`. - * - * @param {Object} traits - */ - -Entity.prototype._setTraits = function(traits) { - traits = traits || {}; - if (this._options.persist) { - store.set(this._options.localStorage.key, traits); - } else { - this._traits = traits; - } -}; - - -/** - * Identify the entity with an `id` and `traits`. If we it's the same entity, - * extend the existing `traits` instead of overwriting. - * - * @param {String} id - * @param {Object} traits - */ - -Entity.prototype.identify = function(id, traits) { - traits = traits || {}; - var current = this.id(); - if (current === null || current === id) traits = extend(this.traits(), traits); - if (id) this.id(id); - this.debug('identify %o, %o', id, traits); - this.traits(traits); - this.save(); -}; - - -/** - * Save the entity to local storage and the cookie. - * - * @return {Boolean} - */ - -Entity.prototype.save = function() { - if (!this._options.persist) return false; - cookie.set(this._options.cookie.key, this.id()); - store.set(this._options.localStorage.key, this.traits()); - return true; -}; - - -/** - * Log the entity out, reseting `id` and `traits` to defaults. - */ - -Entity.prototype.logout = function() { - this.id(null); - this.traits({}); - cookie.remove(this._options.cookie.key); - store.remove(this._options.localStorage.key); -}; - - -/** - * Reset all entity state, logging out and returning options to defaults. - */ - -Entity.prototype.reset = function() { - this.logout(); - this.options({}); -}; - - -/** - * Load saved entity `id` or `traits` from storage. - */ - -Entity.prototype.load = function() { - this.id(cookie.get(this._options.cookie.key)); - this.traits(store.get(this._options.localStorage.key)); -}; - - -}, {"clone":13,"./cookie":14,"debug":15,"defaults":16,"extend":68,"./memory":21,"./store":28,"isodate-traverse":38}], -68: [function(require, module, exports) { - -module.exports = function extend (object) { - // Takes an unlimited number of extenders. - var args = Array.prototype.slice.call(arguments, 1); - - // For each extender, copy their properties on our object. - for (var i = 0, source; source = args[i]; i++) { - if (!source) continue; - for (var property in source) { - object[property] = source[property]; - } - } - - return object; -}; -}, {}], -21: [function(require, module, exports) { -/* eslint consistent-return:1 */ - -/** - * Module Dependencies. - */ - -var bind = require('bind'); -var clone = require('clone'); - -/** - * HOP. - */ - -var has = Object.prototype.hasOwnProperty; - -/** - * Expose `Memory` - */ - -module.exports = bind.all(new Memory()); - -/** - * Initialize `Memory` store - */ - -function Memory(){ - this.store = {}; -} - -/** - * Set a `key` and `value`. - * - * @param {String} key - * @param {Mixed} value - * @return {Boolean} - */ - -Memory.prototype.set = function(key, value){ - this.store[key] = clone(value); - return true; -}; - -/** - * Get a `key`. - * - * @param {String} key - */ - -Memory.prototype.get = function(key){ - if (!has.call(this.store, key)) return; - return clone(this.store[key]); -}; - -/** - * Remove a `key`. - * - * @param {String} key - * @return {Boolean} - */ - -Memory.prototype.remove = function(key){ - delete this.store[key]; - return true; -}; - -}, {"bind":11,"clone":13}], -28: [function(require, module, exports) { - -/** - * Module dependencies. - */ - -var bind = require('bind'); -var defaults = require('defaults'); -var store = require('store.js'); - -/** - * Initialize a new `Store` with `options`. - * - * @param {Object} options - */ - -function Store(options) { - this.options(options); -} - -/** - * Set the `options` for the store. - * - * @param {Object} options - * @field {Boolean} enabled (true) - */ - -Store.prototype.options = function(options) { - if (arguments.length === 0) return this._options; - - options = options || {}; - defaults(options, { enabled: true }); - - this.enabled = options.enabled && store.enabled; - this._options = options; -}; - - -/** - * Set a `key` and `value` in local storage. - * - * @param {string} key - * @param {Object} value - */ - -Store.prototype.set = function(key, value) { - if (!this.enabled) return false; - return store.set(key, value); -}; - - -/** - * Get a value from local storage by `key`. - * - * @param {string} key - * @return {Object} - */ - -Store.prototype.get = function(key) { - if (!this.enabled) return null; - return store.get(key); -}; - - -/** - * Remove a value from local storage by `key`. - * - * @param {string} key - */ - -Store.prototype.remove = function(key) { - if (!this.enabled) return false; - return store.remove(key); -}; - - -/** - * Expose the store singleton. - */ - -module.exports = bind.all(new Store()); - - -/** - * Expose the `Store` constructor. - */ - -module.exports.Store = Store; - -}, {"bind":11,"defaults":16,"store.js":69}], -69: [function(require, module, exports) { -var json = require('json') - , store = {} - , win = window - , doc = win.document - , localStorageName = 'localStorage' - , namespace = '__storejs__' - , storage; - -store.disabled = false -store.set = function(key, value) {} -store.get = function(key) {} -store.remove = function(key) {} -store.clear = function() {} -store.transact = function(key, defaultVal, transactionFn) { - var val = store.get(key) - if (transactionFn == null) { - transactionFn = defaultVal - defaultVal = null - } - if (typeof val == 'undefined') { val = defaultVal || {} } - transactionFn(val) - store.set(key, val) -} -store.getAll = function() {} - -store.serialize = function(value) { - return json.stringify(value) -} -store.deserialize = function(value) { - if (typeof value != 'string') { return undefined } - try { return json.parse(value) } - catch(e) { return value || undefined } -} - -// Functions to encapsulate questionable FireFox 3.6.13 behavior -// when about.config::dom.storage.enabled === false -// See https://github.com/marcuswestin/store.js/issues#issue/13 -function isLocalStorageNameSupported() { - try { return (localStorageName in win && win[localStorageName]) } - catch(err) { return false } -} - -if (isLocalStorageNameSupported()) { - storage = win[localStorageName] - store.set = function(key, val) { - if (val === undefined) { return store.remove(key) } - storage.setItem(key, store.serialize(val)) - return val - } - store.get = function(key) { return store.deserialize(storage.getItem(key)) } - store.remove = function(key) { storage.removeItem(key) } - store.clear = function() { storage.clear() } - store.getAll = function() { - var ret = {} - for (var i=0; idocument.w=window') - storageContainer.close() - storageOwner = storageContainer.w.frames[0].document - storage = storageOwner.createElement('div') - } catch(e) { - // somehow ActiveXObject instantiation failed (perhaps some special - // security settings or otherwse), fall back to per-path storage - storage = doc.createElement('div') - storageOwner = doc.body - } - function withIEStorage(storeFunction) { - return function() { - var args = Array.prototype.slice.call(arguments, 0) - args.unshift(storage) - // See http://msdn.microsoft.com/en-us/library/ms531081(v=VS.85).aspx - // and http://msdn.microsoft.com/en-us/library/ms531424(v=VS.85).aspx - storageOwner.appendChild(storage) - storage.addBehavior('#default#userData') - storage.load(localStorageName) - var result = storeFunction.apply(store, args) - storageOwner.removeChild(storage) - return result - } - } - - // In IE7, keys may not contain special chars. See all of https://github.com/marcuswestin/store.js/issues/40 - var forbiddenCharsRegex = new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]", "g") - function ieKeyFix(key) { - return key.replace(forbiddenCharsRegex, '___') - } - store.set = withIEStorage(function(storage, key, val) { - key = ieKeyFix(key) - if (val === undefined) { return store.remove(key) } - storage.setAttribute(key, store.serialize(val)) - storage.save(localStorageName) - return val - }) - store.get = withIEStorage(function(storage, key) { - key = ieKeyFix(key) - return store.deserialize(storage.getAttribute(key)) - }) - store.remove = withIEStorage(function(storage, key) { - key = ieKeyFix(key) - storage.removeAttribute(key) - storage.save(localStorageName) - }) - store.clear = withIEStorage(function(storage) { - var attributes = storage.XMLDocument.documentElement.attributes - storage.load(localStorageName) - for (var i=0, attr; attr=attributes[i]; i++) { - storage.removeAttribute(attr.name) - } - storage.save(localStorageName) - }) - store.getAll = withIEStorage(function(storage) { - var attributes = storage.XMLDocument.documentElement.attributes - var ret = {} - for (var i=0, attr; attr=attributes[i]; ++i) { - var key = ieKeyFix(attr.name) - ret[attr.name] = store.deserialize(storage.getAttribute(key)) - } - return ret - }) -} - -try { - store.set(namespace, namespace) - if (store.get(namespace) != namespace) { store.disabled = true } - store.remove(namespace) -} catch(e) { - store.disabled = true -} -store.enabled = !store.disabled - -module.exports = store; -}, {"json":59}], -67: [function(require, module, exports) { - -module.exports = function(a, b){ - var fn = function(){}; - fn.prototype = b.prototype; - a.prototype = new fn; - a.prototype.constructor = a; -}; -}, {}], -18: [function(require, module, exports) { - -var isEmpty = require('is-empty'); - -try { - var typeOf = require('type'); -} catch (e) { - var typeOf = require('component-type'); -} - - -/** - * Types. - */ - -var types = [ - 'arguments', - 'array', - 'boolean', - 'date', - 'element', - 'function', - 'null', - 'number', - 'object', - 'regexp', - 'string', - 'undefined' -]; - - -/** - * Expose type checkers. - * - * @param {Mixed} value - * @return {Boolean} - */ - -for (var i = 0, type; type = types[i]; i++) exports[type] = generate(type); - - -/** - * Add alias for `function` for old browsers. - */ - -exports.fn = exports['function']; - - -/** - * Expose `empty` check. - */ - -exports.empty = isEmpty; - - -/** - * Expose `nan` check. - */ - -exports.nan = function (val) { - return exports.number(val) && val != val; -}; - - -/** - * Generate a type checker. - * - * @param {String} type - * @return {Function} - */ - -function generate (type) { - return function (value) { - return type === typeOf(value); - }; -} -}, {"is-empty":46,"type":47,"component-type":47}], -19: [function(require, module, exports) { -module.exports = function isMeta (e) { - if (e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) return true; - - // Logic that handles checks for the middle mouse button, based - // on [jQuery](https://github.com/jquery/jquery/blob/master/src/event.js#L466). - var which = e.which, button = e.button; - if (!which && button !== undefined) { - return (!button & 1) && (!button & 2) && (button & 4); - } else if (which === 2) { - return true; - } - - return false; -}; -}, {}], -20: [function(require, module, exports) { - -/** - * HOP ref. - */ - -var has = Object.prototype.hasOwnProperty; - -/** - * Return own keys in `obj`. - * - * @param {Object} obj - * @return {Array} - * @api public - */ - -exports.keys = Object.keys || function(obj){ - var keys = []; - for (var key in obj) { - if (has.call(obj, key)) { - keys.push(key); - } - } - return keys; -}; - -/** - * Return own values in `obj`. - * - * @param {Object} obj - * @return {Array} - * @api public - */ - -exports.values = function(obj){ - var vals = []; - for (var key in obj) { - if (has.call(obj, key)) { - vals.push(obj[key]); - } - } - return vals; -}; - -/** - * Merge `b` into `a`. - * - * @param {Object} a - * @param {Object} b - * @return {Object} a - * @api public - */ - -exports.merge = function(a, b){ - for (var key in b) { - if (has.call(b, key)) { - a[key] = b[key]; - } - } - return a; -}; - -/** - * Return length of `obj`. - * - * @param {Object} obj - * @return {Number} - * @api public - */ - -exports.length = function(obj){ - return exports.keys(obj).length; -}; - -/** - * Check if `obj` is empty. - * - * @param {Object} obj - * @return {Boolean} - * @api public - */ - -exports.isEmpty = function(obj){ - return 0 == exports.length(obj); -}; -}, {}], -22: [function(require, module, exports) { - -/** - * Module Dependencies. - */ - -var debug = require('debug')('analytics.js:normalize'); -var defaults = require('defaults'); -var each = require('each'); -var includes = require('includes'); -var is = require('is'); -var map = require('component/map'); - -/** - * HOP. - */ - -var has = Object.prototype.hasOwnProperty; - -/** - * Expose `normalize` - */ - -module.exports = normalize; - -/** - * Toplevel properties. - */ - -var toplevel = [ - 'integrations', - 'anonymousId', - 'timestamp', - 'context' -]; - -/** - * Normalize `msg` based on integrations `list`. - * - * @param {Object} msg - * @param {Array} list - * @return {Function} - */ - -function normalize(msg, list){ - var lower = map(list, function(s){ return s.toLowerCase(); }); - var opts = msg.options || {}; - var integrations = opts.integrations || {}; - var providers = opts.providers || {}; - var context = opts.context || {}; - var ret = {}; - debug('<-', msg); - - // integrations. - each(opts, function(key, value){ - if (!integration(key)) return; - if (!has.call(integrations, key)) integrations[key] = value; - delete opts[key]; - }); - - // providers. - delete opts.providers; - each(providers, function(key, value){ - if (!integration(key)) return; - if (is.object(integrations[key])) return; - if (has.call(integrations, key) && typeof providers[key] === 'boolean') return; - integrations[key] = value; - }); - - // move all toplevel options to msg - // and the rest to context. - each(opts, function(key){ - if (includes(key, toplevel)) { - ret[key] = opts[key]; - } else { - context[key] = opts[key]; - } - }); - - // cleanup - delete msg.options; - ret.integrations = integrations; - ret.context = context; - ret = defaults(ret, msg); - debug('->', ret); - return ret; - - function integration(name){ - return !!(includes(name, list) || name.toLowerCase() === 'all' || includes(name.toLowerCase(), lower)); - } -} - -}, {"debug":15,"defaults":16,"each":4,"includes":70,"is":18,"component/map":71}], -70: [function(require, module, exports) { -'use strict'; - -/** - * Module dependencies. - */ - -// XXX: Hacky fix for duo not supporting scoped npm packages -var each; try { each = require('@ndhoule/each'); } catch(e) { each = require('each'); } - -/** - * String#indexOf reference. - */ - -var strIndexOf = String.prototype.indexOf; - -/** - * Object.is/sameValueZero polyfill. - * - * @api private - * @param {*} value1 - * @param {*} value2 - * @return {boolean} - */ - -// TODO: Move to library -var sameValueZero = function sameValueZero(value1, value2) { - // Normal values and check for 0 / -0 - if (value1 === value2) { - return value1 !== 0 || 1 / value1 === 1 / value2; - } - // NaN - return value1 !== value1 && value2 !== value2; -}; - -/** - * Searches a given `collection` for a value, returning true if the collection - * contains the value and false otherwise. Can search strings, arrays, and - * objects. - * - * @name includes - * @api public - * @param {*} searchElement The element to search for. - * @param {Object|Array|string} collection The collection to search. - * @return {boolean} - * @example - * includes(2, [1, 2, 3]); - * //=> true - * - * includes(4, [1, 2, 3]); - * //=> false - * - * includes(2, { a: 1, b: 2, c: 3 }); - * //=> true - * - * includes('a', { a: 1, b: 2, c: 3 }); - * //=> false - * - * includes('abc', 'xyzabc opq'); - * //=> true - * - * includes('nope', 'xyzabc opq'); - * //=> false - */ -var includes = function includes(searchElement, collection) { - var found = false; - - // Delegate to String.prototype.indexOf when `collection` is a string - if (typeof collection === 'string') { - return strIndexOf.call(collection, searchElement) !== -1; - } - - // Iterate through enumerable/own array elements and object properties. - each(function(value) { - if (sameValueZero(value, searchElement)) { - found = true; - // Exit iteration early when found - return false; - } - }, collection); - - return found; -}; - -/** - * Exports. - */ - -module.exports = includes; - -}, {"each":72}], -72: [function(require, module, exports) { -'use strict'; - -/** - * Module dependencies. - */ - -// XXX: Hacky fix for Duo not supporting scoped modules -var keys; try { keys = require('@ndhoule/keys'); } catch(e) { keys = require('keys'); } - -/** - * Object.prototype.toString reference. - */ - -var objToString = Object.prototype.toString; - -/** - * Tests if a value is a number. - * - * @name isNumber - * @api private - * @param {*} val The value to test. - * @return {boolean} Returns `true` if `val` is a number, otherwise `false`. - */ - -// TODO: Move to library -var isNumber = function isNumber(val) { - var type = typeof val; - return type === 'number' || (type === 'object' && objToString.call(val) === '[object Number]'); -}; - -/** - * Tests if a value is an array. - * - * @name isArray - * @api private - * @param {*} val The value to test. - * @return {boolean} Returns `true` if the value is an array, otherwise `false`. - */ - -// TODO: Move to library -var isArray = typeof Array.isArray === 'function' ? Array.isArray : function isArray(val) { - return objToString.call(val) === '[object Array]'; -}; - -/** - * Tests if a value is array-like. Array-like means the value is not a function and has a numeric - * `.length` property. - * - * @name isArrayLike - * @api private - * @param {*} val - * @return {boolean} - */ - -// TODO: Move to library -var isArrayLike = function isArrayLike(val) { - return val != null && (isArray(val) || (val !== 'function' && isNumber(val.length))); -}; - -/** - * Internal implementation of `each`. Works on arrays and array-like data structures. - * - * @name arrayEach - * @api private - * @param {Function(value, key, collection)} iterator The function to invoke per iteration. - * @param {Array} array The array(-like) structure to iterate over. - * @return {undefined} - */ - -var arrayEach = function arrayEach(iterator, array) { - for (var i = 0; i < array.length; i += 1) { - // Break iteration early if `iterator` returns `false` - if (iterator(array[i], i, array) === false) { - break; - } - } -}; - -/** - * Internal implementation of `each`. Works on objects. - * - * @name baseEach - * @api private - * @param {Function(value, key, collection)} iterator The function to invoke per iteration. - * @param {Object} object The object to iterate over. - * @return {undefined} - */ - -var baseEach = function baseEach(iterator, object) { - var ks = keys(object); - - for (var i = 0; i < ks.length; i += 1) { - // Break iteration early if `iterator` returns `false` - if (iterator(object[ks[i]], ks[i], object) === false) { - break; - } - } -}; - -/** - * Iterate over an input collection, invoking an `iterator` function for each element in the - * collection and passing to it three arguments: `(value, index, collection)`. The `iterator` - * function can end iteration early by returning `false`. - * - * @name each - * @api public - * @param {Function(value, key, collection)} iterator The function to invoke per iteration. - * @param {Array|Object|string} collection The collection to iterate over. - * @return {undefined} Because `each` is run only for side effects, always returns `undefined`. - * @example - * var log = console.log.bind(console); - * - * each(log, ['a', 'b', 'c']); - * //-> 'a', 0, ['a', 'b', 'c'] - * //-> 'b', 1, ['a', 'b', 'c'] - * //-> 'c', 2, ['a', 'b', 'c'] - * //=> undefined - * - * each(log, 'tim'); - * //-> 't', 2, 'tim' - * //-> 'i', 1, 'tim' - * //-> 'm', 0, 'tim' - * //=> undefined - * - * // Note: Iteration order not guaranteed across environments - * each(log, { name: 'tim', occupation: 'enchanter' }); - * //-> 'tim', 'name', { name: 'tim', occupation: 'enchanter' } - * //-> 'enchanter', 'occupation', { name: 'tim', occupation: 'enchanter' } - * //=> undefined - */ - -var each = function each(iterator, collection) { - return (isArrayLike(collection) ? arrayEach : baseEach).call(this, iterator, collection); -}; - -/** - * Exports. - */ - -module.exports = each; - -}, {"keys":73}], -73: [function(require, module, exports) { -'use strict'; - -/** - * charAt reference. - */ - -var strCharAt = String.prototype.charAt; - -/** - * Returns the character at a given index. - * - * @param {string} str - * @param {number} index - * @return {string|undefined} - */ - -// TODO: Move to a library -var charAt = function(str, index) { - return strCharAt.call(str, index); -}; - -/** - * hasOwnProperty reference. - */ - -var hop = Object.prototype.hasOwnProperty; - -/** - * Object.prototype.toString reference. - */ - -var toStr = Object.prototype.toString; - -/** - * hasOwnProperty, wrapped as a function. - * - * @name has - * @api private - * @param {*} context - * @param {string|number} prop - * @return {boolean} - */ - -// TODO: Move to a library -var has = function has(context, prop) { - return hop.call(context, prop); -}; - -/** - * Returns true if a value is a string, otherwise false. - * - * @name isString - * @api private - * @param {*} val - * @return {boolean} - */ - -// TODO: Move to a library -var isString = function isString(val) { - return toStr.call(val) === '[object String]'; -}; - -/** - * Returns true if a value is array-like, otherwise false. Array-like means a - * value is not null, undefined, or a function, and has a numeric `length` - * property. - * - * @name isArrayLike - * @api private - * @param {*} val - * @return {boolean} - */ - -// TODO: Move to a library -var isArrayLike = function isArrayLike(val) { - return val != null && (typeof val !== 'function' && typeof val.length === 'number'); -}; - - -/** - * indexKeys - * - * @name indexKeys - * @api private - * @param {} target - * @param {} pred - * @return {Array} - */ - -var indexKeys = function indexKeys(target, pred) { - pred = pred || has; - var results = []; - - for (var i = 0, len = target.length; i < len; i += 1) { - if (pred(target, i)) { - results.push(String(i)); - } - } - - return results; -}; - -/** - * Returns an array of all the owned - * - * @name objectKeys - * @api private - * @param {*} target - * @param {Function} pred Predicate function used to include/exclude values from - * the resulting array. - * @return {Array} - */ - -var objectKeys = function objectKeys(target, pred) { - pred = pred || has; - var results = []; - - - for (var key in target) { - if (pred(target, key)) { - results.push(String(key)); - } - } - - return results; -}; - -/** - * Creates an array composed of all keys on the input object. Ignores any non-enumerable properties. - * More permissive than the native `Object.keys` function (non-objects will not throw errors). - * - * @name keys - * @api public - * @category Object - * @param {Object} source The value to retrieve keys from. - * @return {Array} An array containing all the input `source`'s keys. - * @example - * keys({ likes: 'avocado', hates: 'pineapple' }); - * //=> ['likes', 'pineapple']; - * - * // Ignores non-enumerable properties - * var hasHiddenKey = { name: 'Tim' }; - * Object.defineProperty(hasHiddenKey, 'hidden', { - * value: 'i am not enumerable!', - * enumerable: false - * }) - * keys(hasHiddenKey); - * //=> ['name']; - * - * // Works on arrays - * keys(['a', 'b', 'c']); - * //=> ['0', '1', '2'] - * - * // Skips unpopulated indices in sparse arrays - * var arr = [1]; - * arr[4] = 4; - * keys(arr); - * //=> ['0', '4'] - */ - -module.exports = function keys(source) { - if (source == null) { - return []; - } - - // IE6-8 compatibility (string) - if (isString(source)) { - return indexKeys(source, charAt); - } - - // IE6-8 compatibility (arguments) - if (isArrayLike(source)) { - return indexKeys(source, has); - } - - return objectKeys(source); -}; - -}, {}], -71: [function(require, module, exports) { - -/** - * Module dependencies. - */ - -var toFunction = require('to-function'); - -/** - * Map the given `arr` with callback `fn(val, i)`. - * - * @param {Array} arr - * @param {Function} fn - * @return {Array} - * @api public - */ - -module.exports = function(arr, fn){ - var ret = []; - fn = toFunction(fn); - for (var i = 0; i < arr.length; ++i) { - ret.push(fn(arr[i], i)); - } - return ret; -}; -}, {"to-function":74}], -74: [function(require, module, exports) { - -/** - * Module Dependencies - */ - -var expr; -try { - expr = require('props'); -} catch(e) { - expr = require('component-props'); -} - -/** - * Expose `toFunction()`. - */ - -module.exports = toFunction; - -/** - * Convert `obj` to a `Function`. - * - * @param {Mixed} obj - * @return {Function} - * @api private - */ - -function toFunction(obj) { - switch ({}.toString.call(obj)) { - case '[object Object]': - return objectToFunction(obj); - case '[object Function]': - return obj; - case '[object String]': - return stringToFunction(obj); - case '[object RegExp]': - return regexpToFunction(obj); - default: - return defaultToFunction(obj); - } -} - -/** - * Default to strict equality. - * - * @param {Mixed} val - * @return {Function} - * @api private - */ - -function defaultToFunction(val) { - return function(obj){ - return val === obj; - }; -} - -/** - * Convert `re` to a function. - * - * @param {RegExp} re - * @return {Function} - * @api private - */ - -function regexpToFunction(re) { - return function(obj){ - return re.test(obj); - }; -} - -/** - * Convert property `str` to a function. - * - * @param {String} str - * @return {Function} - * @api private - */ - -function stringToFunction(str) { - // immediate such as "> 20" - if (/^ *\W+/.test(str)) return new Function('_', 'return _ ' + str); - - // properties such as "name.first" or "age > 18" or "age > 18 && age < 36" - return new Function('_', 'return ' + get(str)); -} - -/** - * Convert `object` to a function. - * - * @param {Object} object - * @return {Function} - * @api private - */ - -function objectToFunction(obj) { - var match = {}; - for (var key in obj) { - match[key] = typeof obj[key] === 'string' - ? defaultToFunction(obj[key]) - : toFunction(obj[key]); - } - return function(val){ - if (typeof val !== 'object') return false; - for (var key in match) { - if (!(key in val)) return false; - if (!match[key](val[key])) return false; - } - return true; - }; -} - -/** - * Built the getter function. Supports getter style functions - * - * @param {String} str - * @return {String} - * @api private - */ - -function get(str) { - var props = expr(str); - if (!props.length) return '_.' + str; - - var val, i, prop; - for (i = 0; i < props.length; i++) { - prop = props[i]; - val = '_.' + prop; - val = "('function' == typeof " + val + " ? " + val + "() : " + val + ")"; - - // mimic negative lookbehind to avoid problems with nested properties - str = stripNested(prop, str, val); - } - - return str; -} - -/** - * Mimic negative lookbehind to avoid problems with nested properties. - * - * See: http://blog.stevenlevithan.com/archives/mimic-lookbehind-javascript - * - * @param {String} prop - * @param {String} str - * @param {String} val - * @return {String} - * @api private - */ - -function stripNested (prop, str, val) { - return str.replace(new RegExp('(\\.)?' + prop, 'g'), function($0, $1) { - return $1 ? $0 : val; - }); -} - -}, {"props":75,"component-props":75}], -75: [function(require, module, exports) { -/** - * Global Names - */ - -var globals = /\b(this|Array|Date|Object|Math|JSON)\b/g; - -/** - * Return immediate identifiers parsed from `str`. - * - * @param {String} str - * @param {String|Function} map function or prefix - * @return {Array} - * @api public - */ - -module.exports = function(str, fn){ - var p = unique(props(str)); - if (fn && 'string' == typeof fn) fn = prefixed(fn); - if (fn) return map(str, p, fn); - return p; -}; - -/** - * Return immediate identifiers in `str`. - * - * @param {String} str - * @return {Array} - * @api private - */ - -function props(str) { - return str - .replace(/\.\w+|\w+ *\(|"[^"]*"|'[^']*'|\/([^/]+)\//g, '') - .replace(globals, '') - .match(/[$a-zA-Z_]\w*/g) - || []; -} - -/** - * Return `str` with `props` mapped with `fn`. - * - * @param {String} str - * @param {Array} props - * @param {Function} fn - * @return {String} - * @api private - */ - -function map(str, props, fn) { - var re = /\.\w+|\w+ *\(|"[^"]*"|'[^']*'|\/([^/]+)\/|[a-zA-Z_]\w*/g; - return str.replace(re, function(_){ - if ('(' == _[_.length - 1]) return fn(_); - if (!~props.indexOf(_)) return _; - return fn(_); - }); -} - -/** - * Return unique array. - * - * @param {Array} arr - * @return {Array} - * @api private - */ - -function unique(arr) { - var ret = []; - - for (var i = 0; i < arr.length; i++) { - if (~ret.indexOf(arr[i])) continue; - ret.push(arr[i]); - } - - return ret; -} - -/** - * Map with prefix `str`. - */ - -function prefixed(str) { - return function(_){ - return str + _; - }; -} - -}, {}], -23: [function(require, module, exports) { - -/** - * Bind `el` event `type` to `fn`. - * - * @param {Element} el - * @param {String} type - * @param {Function} fn - * @param {Boolean} capture - * @return {Function} - * @api public - */ - -exports.bind = function(el, type, fn, capture){ - if (el.addEventListener) { - el.addEventListener(type, fn, capture || false); - } else { - el.attachEvent('on' + type, fn); - } - return fn; -}; - -/** - * Unbind `el` event `type`'s callback `fn`. - * - * @param {Element} el - * @param {String} type - * @param {Function} fn - * @param {Boolean} capture - * @return {Function} - * @api public - */ - -exports.unbind = function(el, type, fn, capture){ - if (el.removeEventListener) { - el.removeEventListener(type, fn, capture || false); - } else { - el.detachEvent('on' + type, fn); - } - return fn; -}; - -}, {}], -24: [function(require, module, exports) { - -/** - * Module dependencies. - */ - -var canonical = require('canonical'); -var includes = require('includes'); -var url = require('url'); - -/** - * Return a default `options.context.page` object. - * - * https://segment.com/docs/spec/page/#properties - * - * @return {Object} - */ - -function pageDefaults() { - return { - path: canonicalPath(), - referrer: document.referrer, - search: location.search, - title: document.title, - url: canonicalUrl(location.search) - }; -} - -/** - * Return the canonical path for the page. - * - * @return {string} - */ - -function canonicalPath() { - var canon = canonical(); - if (!canon) return window.location.pathname; - var parsed = url.parse(canon); - return parsed.pathname; -} - -/** - * Return the canonical URL for the page concat the given `search` - * and strip the hash. - * - * @param {string} search - * @return {string} - */ - -function canonicalUrl(search) { - var canon = canonical(); - if (canon) return includes('?', canon) ? canon : canon + search; - var url = window.location.href; - var i = url.indexOf('#'); - return i === -1 ? url : url.slice(0, i); -} - -/** - * Exports. - */ - -module.exports = pageDefaults; - -}, {"canonical":76,"includes":70,"url":77}], -76: [function(require, module, exports) { -module.exports = function canonical () { - var tags = document.getElementsByTagName('link'); - for (var i = 0, tag; tag = tags[i]; i++) { - if ('canonical' == tag.getAttribute('rel')) return tag.getAttribute('href'); - } -}; -}, {}], -77: [function(require, module, exports) { - -/** - * Parse the given `url`. - * - * @param {String} str - * @return {Object} - * @api public - */ - -exports.parse = function(url){ - var a = document.createElement('a'); - a.href = url; - return { - href: a.href, - host: a.host || location.host, - port: ('0' === a.port || '' === a.port) ? port(a.protocol) : a.port, - hash: a.hash, - hostname: a.hostname || location.hostname, - pathname: a.pathname.charAt(0) != '/' ? '/' + a.pathname : a.pathname, - protocol: !a.protocol || ':' == a.protocol ? location.protocol : a.protocol, - search: a.search, - query: a.search.slice(1) - }; -}; - -/** - * Check if `url` is absolute. - * - * @param {String} url - * @return {Boolean} - * @api public - */ - -exports.isAbsolute = function(url){ - return 0 == url.indexOf('//') || !!~url.indexOf('://'); -}; - -/** - * Check if `url` is relative. - * - * @param {String} url - * @return {Boolean} - * @api public - */ - -exports.isRelative = function(url){ - return !exports.isAbsolute(url); -}; - -/** - * Check if `url` is cross domain. - * - * @param {String} url - * @return {Boolean} - * @api public - */ - -exports.isCrossDomain = function(url){ - url = exports.parse(url); - var location = exports.parse(window.location.href); - return url.hostname !== location.hostname - || url.port !== location.port - || url.protocol !== location.protocol; -}; - -/** - * Return default port for `protocol`. - * - * @param {String} protocol - * @return {String} - * @api private - */ -function port (protocol){ - switch (protocol) { - case 'http:': - return 80; - case 'https:': - return 443; - default: - return location.port; - } -} - -}, {}], -25: [function(require, module, exports) { -'use strict'; - -var objToString = Object.prototype.toString; - -// TODO: Move to lib -var existy = function(val) { - return val != null; -}; - -// TODO: Move to lib -var isArray = function(val) { - return objToString.call(val) === '[object Array]'; -}; - -// TODO: Move to lib -var isString = function(val) { - return typeof val === 'string' || objToString.call(val) === '[object String]'; -}; - -// TODO: Move to lib -var isObject = function(val) { - return val != null && typeof val === 'object'; -}; - -/** - * Returns a copy of the new `object` containing only the specified properties. - * - * @name pick - * @api public - * @category Object - * @see {@link omit} - * @param {Array.|string} props The property or properties to keep. - * @param {Object} object The object to iterate over. - * @return {Object} A new object containing only the specified properties from `object`. - * @example - * var person = { name: 'Tim', occupation: 'enchanter', fears: 'rabbits' }; - * - * pick('name', person); - * //=> { name: 'Tim' } - * - * pick(['name', 'fears'], person); - * //=> { name: 'Tim', fears: 'rabbits' } - */ - -var pick = function pick(props, object) { - if (!existy(object) || !isObject(object)) { - return {}; - } - - if (isString(props)) { - props = [props]; - } - - if (!isArray(props)) { - props = []; - } - - var result = {}; - - for (var i = 0; i < props.length; i += 1) { - if (isString(props[i]) && props[i] in object) { - result[props[i]] = object[props[i]]; - } - } - - return result; -}; - -/** - * Exports. - */ - -module.exports = pick; - -}, {}], -26: [function(require, module, exports) { - -/** - * prevent default on the given `e`. - * - * examples: - * - * anchor.onclick = prevent; - * anchor.onclick = function(e){ - * if (something) return prevent(e); - * }; - * - * @param {Event} e - */ - -module.exports = function(e){ - e = e || window.event - return e.preventDefault - ? e.preventDefault() - : e.returnValue = false; -}; - -}, {}], -27: [function(require, module, exports) { - -/** - * Module dependencies. - */ - -var trim = require('trim'); -var type = require('type'); - -var pattern = /(\w+)\[(\d+)\]/ - -/** - * Safely encode the given string - * - * @param {String} str - * @return {String} - * @api private - */ - -var encode = function(str) { - try { - return encodeURIComponent(str); - } catch (e) { - return str; - } -}; - -/** - * Safely decode the string - * - * @param {String} str - * @return {String} - * @api private - */ - -var decode = function(str) { - try { - return decodeURIComponent(str.replace(/\+/g, ' ')); - } catch (e) { - return str; - } -} - -/** - * Parse the given query `str`. - * - * @param {String} str - * @return {Object} - * @api public - */ - -exports.parse = function(str){ - if ('string' != typeof str) return {}; - - str = trim(str); - if ('' == str) return {}; - if ('?' == str.charAt(0)) str = str.slice(1); - - var obj = {}; - var pairs = str.split('&'); - for (var i = 0; i < pairs.length; i++) { - var parts = pairs[i].split('='); - var key = decode(parts[0]); - var m; - - if (m = pattern.exec(key)) { - obj[m[1]] = obj[m[1]] || []; - obj[m[1]][m[2]] = decode(parts[1]); - continue; - } - - obj[parts[0]] = null == parts[1] - ? '' - : decode(parts[1]); - } - - return obj; -}; - -/** - * Stringify the given `obj`. - * - * @param {Object} obj - * @return {String} - * @api public - */ - -exports.stringify = function(obj){ - if (!obj) return ''; - var pairs = []; - - for (var key in obj) { - var value = obj[key]; - - if ('array' == type(value)) { - for (var i = 0; i < value.length; ++i) { - pairs.push(encode(key + '[' + i + ']') + '=' + encode(value[i])); - } - continue; - } - - pairs.push(encode(key) + '=' + encode(obj[key])); - } - - return pairs.join('&'); -}; - -}, {"trim":54,"type":47}], -29: [function(require, module, exports) { - -/** - * Module dependencies. - */ - -var Entity = require('./entity'); -var bind = require('bind'); -var cookie = require('./cookie'); -var debug = require('debug')('analytics:user'); -var inherit = require('inherit'); -var rawCookie = require('cookie'); -var uuid = require('uuid'); - - -/** - * User defaults - */ - -User.defaults = { - persist: true, - cookie: { - key: 'ajs_user_id', - oldKey: 'ajs_user' - }, - localStorage: { - key: 'ajs_user_traits' - } -}; - - -/** - * Initialize a new `User` with `options`. - * - * @param {Object} options - */ - -function User(options) { - this.defaults = User.defaults; - this.debug = debug; - Entity.call(this, options); -} - - -/** - * Inherit `Entity` - */ - -inherit(User, Entity); - -/** - * Set/get the user id. - * - * When the user id changes, the method will reset his anonymousId to a new one. - * - * // FIXME: What are the mixed types? - * @param {string} id - * @return {Mixed} - * @example - * // didn't change because the user didn't have previous id. - * anonymousId = user.anonymousId(); - * user.id('foo'); - * assert.equal(anonymousId, user.anonymousId()); - * - * // didn't change because the user id changed to null. - * anonymousId = user.anonymousId(); - * user.id('foo'); - * user.id(null); - * assert.equal(anonymousId, user.anonymousId()); - * - * // change because the user had previous id. - * anonymousId = user.anonymousId(); - * user.id('foo'); - * user.id('baz'); // triggers change - * user.id('baz'); // no change - * assert.notEqual(anonymousId, user.anonymousId()); - */ - -User.prototype.id = function(id){ - var prev = this._getId(); - var ret = Entity.prototype.id.apply(this, arguments); - if (prev == null) return ret; - // FIXME: We're relying on coercion here (1 == "1"), but our API treats these - // two values differently. Figure out what will break if we remove this and - // change to strict equality - /* eslint-disable eqeqeq */ - if (prev != id && id) this.anonymousId(null); - /* eslint-enable eqeqeq */ - return ret; -}; - -/** - * Set / get / remove anonymousId. - * - * @param {String} anonymousId - * @return {String|User} - */ - -User.prototype.anonymousId = function(anonymousId){ - var store = this.storage(); - - // set / remove - if (arguments.length) { - store.set('ajs_anonymous_id', anonymousId); - return this; - } - - // new - anonymousId = store.get('ajs_anonymous_id'); - if (anonymousId) { - return anonymousId; - } - - // old - it is not stringified so we use the raw cookie. - anonymousId = rawCookie('_sio'); - if (anonymousId) { - anonymousId = anonymousId.split('----')[0]; - store.set('ajs_anonymous_id', anonymousId); - store.remove('_sio'); - return anonymousId; - } - - // empty - anonymousId = uuid(); - store.set('ajs_anonymous_id', anonymousId); - return store.get('ajs_anonymous_id'); -}; - -/** - * Remove anonymous id on logout too. - */ - -User.prototype.logout = function(){ - Entity.prototype.logout.call(this); - this.anonymousId(null); -}; - -/** - * Load saved user `id` or `traits` from storage. - */ - -User.prototype.load = function() { - if (this._loadOldCookie()) return; - Entity.prototype.load.call(this); -}; - - -/** - * BACKWARDS COMPATIBILITY: Load the old user from the cookie. - * - * @api private - * @return {boolean} - */ - -User.prototype._loadOldCookie = function() { - var user = cookie.get(this._options.cookie.oldKey); - if (!user) return false; - - this.id(user.id); - this.traits(user.traits); - cookie.remove(this._options.cookie.oldKey); - return true; -}; - - -/** - * Expose the user singleton. - */ - -module.exports = bind.all(new User()); - - -/** - * Expose the `User` constructor. - */ - -module.exports.User = User; - -}, {"./entity":66,"bind":11,"./cookie":14,"debug":15,"inherit":67,"cookie":58,"uuid":78}], -78: [function(require, module, exports) { - -/** - * Taken straight from jed's gist: https://gist.github.com/982883 - * - * Returns a random v4 UUID of the form xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx, - * where each x is replaced with a random hexadecimal digit from 0 to f, and - * y is replaced with a random hexadecimal digit from 8 to b. - */ - -module.exports = function uuid(a){ - return a // if the placeholder was passed, return - ? ( // a random number from 0 to 15 - a ^ // unless b is 8, - Math.random() // in which case - * 16 // a random number from - >> a/4 // 8 to 11 - ).toString(16) // in hexadecimal - : ( // or otherwise a concatenated string: - [1e7] + // 10000000 + - -1e3 + // -1000 + - -4e3 + // -4000 + - -8e3 + // -80000000 + - -1e11 // -100000000000, - ).replace( // replacing - /[018]/g, // zeroes, ones, and eights with - uuid // random hex digits - ) -}; -}, {}], -7: [function(require, module, exports) { -module.exports = { - "name": "analytics-core", - "version": "2.10.0", - "main": "analytics.js", - "dependencies": {}, - "devDependencies": {} -} -; -}, {}], -3: [function(require, module, exports) { -/* eslint quote-props: 0 */ -'use strict'; - -module.exports = { - 'adroll': require('analytics.js-integration-adroll'), - 'adwords': require('analytics.js-integration-adwords'), - 'alexa': require('analytics.js-integration-alexa'), - 'amplitude': require('analytics.js-integration-amplitude'), - 'appcues': require('analytics.js-integration-appcues'), - 'atatus': require('analytics.js-integration-atatus'), - 'autosend': require('analytics.js-integration-autosend'), - 'awesm': require('analytics.js-integration-awesm'), - 'bing-ads': require('analytics.js-integration-bing-ads'), - 'blueshift': require('analytics.js-integration-blueshift'), - 'bronto': require('analytics.js-integration-bronto'), - 'bugherd': require('analytics.js-integration-bugherd'), - 'bugsnag': require('analytics.js-integration-bugsnag'), - 'chameleon': require('analytics.js-integration-chameleon'), - 'chartbeat': require('analytics.js-integration-chartbeat'), - 'clicktale': require('analytics.js-integration-clicktale'), - 'clicky': require('analytics.js-integration-clicky'), - 'comscore': require('analytics.js-integration-comscore'), - 'crazy-egg': require('analytics.js-integration-crazy-egg'), - 'curebit': require('analytics.js-integration-curebit'), - 'customerio': require('analytics.js-integration-customerio'), - 'drip': require('analytics.js-integration-drip'), - 'elevio': require('analytics.js-integration-elevio'), - 'errorception': require('analytics.js-integration-errorception'), - 'evergage': require('analytics.js-integration-evergage'), - 'extole': require('analytics.js-integration-extole'), - 'facebook-conversion-tracking': require('analytics.js-integration-facebook-conversion-tracking'), - 'facebook-custom-audiences': require('analytics.js-integration-facebook-custom-audiences'), - 'foxmetrics': require('analytics.js-integration-foxmetrics'), - 'frontleaf': require('analytics.js-integration-frontleaf'), - 'fullstory': require('analytics.js-integration-fullstory'), - 'gauges': require('analytics.js-integration-gauges'), - 'get-satisfaction': require('analytics.js-integration-get-satisfaction'), - 'google-analytics': require('analytics.js-integration-google-analytics'), - 'google-tag-manager': require('analytics.js-integration-google-tag-manager'), - 'gosquared': require('analytics.js-integration-gosquared'), - 'heap': require('analytics.js-integration-heap'), - 'hellobar': require('analytics.js-integration-hellobar'), - 'hittail': require('analytics.js-integration-hittail'), - 'hubspot': require('analytics.js-integration-hubspot'), - 'improvely': require('analytics.js-integration-improvely'), - 'insidevault': require('analytics.js-integration-insidevault'), - 'inspectlet': require('analytics.js-integration-inspectlet'), - 'intercom': require('analytics.js-integration-intercom'), - 'keen-io': require('analytics.js-integration-keen-io'), - 'kenshoo': require('analytics.js-integration-kenshoo'), - 'kissmetrics': require('analytics.js-integration-kissmetrics'), - 'klaviyo': require('analytics.js-integration-klaviyo'), - 'livechat': require('analytics.js-integration-livechat'), - 'lucky-orange': require('analytics.js-integration-lucky-orange'), - 'lytics': require('analytics.js-integration-lytics'), - 'mixpanel': require('analytics.js-integration-mixpanel'), - 'mojn': require('analytics.js-integration-mojn'), - 'mouseflow': require('analytics.js-integration-mouseflow'), - 'mousestats': require('analytics.js-integration-mousestats'), - 'navilytics': require('analytics.js-integration-navilytics'), - 'nudgespot': require('analytics.js-integration-nudgespot'), - 'olark': require('analytics.js-integration-olark'), - 'optimizely': require('analytics.js-integration-optimizely'), - 'outbound': require('analytics.js-integration-outbound'), - 'perfect-audience': require('analytics.js-integration-perfect-audience'), - 'pingdom': require('analytics.js-integration-pingdom'), - 'piwik': require('analytics.js-integration-piwik'), - 'preact': require('analytics.js-integration-preact'), - 'qualaroo': require('analytics.js-integration-qualaroo'), - 'quantcast': require('analytics.js-integration-quantcast'), - 'rollbar': require('analytics.js-integration-rollbar'), - 'route': require('analytics.js-integration-route'), - 'saasquatch': require('analytics.js-integration-saasquatch'), - 'satismeter': require('analytics.js-integration-satismeter'), - 'segmentio': require('analytics.js-integration-segmentio'), - 'sentry': require('analytics.js-integration-sentry'), - 'snapengage': require('analytics.js-integration-snapengage'), - 'spinnakr': require('analytics.js-integration-spinnakr'), - 'supporthero': require('analytics.js-integration-supporthero'), - 'taplytics': require('analytics.js-integration-taplytics'), - 'tapstream': require('analytics.js-integration-tapstream'), - 'trakio': require('analytics.js-integration-trakio'), - 'twitter-ads': require('analytics.js-integration-twitter-ads'), - 'userlike': require('analytics.js-integration-userlike'), - 'uservoice': require('analytics.js-integration-uservoice'), - 'vero': require('analytics.js-integration-vero'), - 'visual-website-optimizer': require('analytics.js-integration-visual-website-optimizer'), - 'webengage': require('analytics.js-integration-webengage'), - 'woopra': require('analytics.js-integration-woopra'), - 'wootric': require('analytics.js-integration-wootric'), - 'yandex-metrica': require('analytics.js-integration-yandex-metrica') -}; - -}, {"analytics.js-integration-adroll":79,"analytics.js-integration-adwords":80,"analytics.js-integration-alexa":81,"analytics.js-integration-amplitude":82,"analytics.js-integration-appcues":83,"analytics.js-integration-atatus":84,"analytics.js-integration-autosend":85,"analytics.js-integration-awesm":86,"analytics.js-integration-bing-ads":87,"analytics.js-integration-blueshift":88,"analytics.js-integration-bronto":89,"analytics.js-integration-bugherd":90,"analytics.js-integration-bugsnag":91,"analytics.js-integration-chameleon":92,"analytics.js-integration-chartbeat":93,"analytics.js-integration-clicktale":94,"analytics.js-integration-clicky":95,"analytics.js-integration-comscore":96,"analytics.js-integration-crazy-egg":97,"analytics.js-integration-curebit":98,"analytics.js-integration-customerio":99,"analytics.js-integration-drip":100,"analytics.js-integration-elevio":101,"analytics.js-integration-errorception":102,"analytics.js-integration-evergage":103,"analytics.js-integration-extole":104,"analytics.js-integration-facebook-conversion-tracking":105,"analytics.js-integration-facebook-custom-audiences":106,"analytics.js-integration-foxmetrics":107,"analytics.js-integration-frontleaf":108,"analytics.js-integration-fullstory":109,"analytics.js-integration-gauges":110,"analytics.js-integration-get-satisfaction":111,"analytics.js-integration-google-analytics":112,"analytics.js-integration-google-tag-manager":113,"analytics.js-integration-gosquared":114,"analytics.js-integration-heap":115,"analytics.js-integration-hellobar":116,"analytics.js-integration-hittail":117,"analytics.js-integration-hubspot":118,"analytics.js-integration-improvely":119,"analytics.js-integration-insidevault":120,"analytics.js-integration-inspectlet":121,"analytics.js-integration-intercom":122,"analytics.js-integration-keen-io":123,"analytics.js-integration-kenshoo":124,"analytics.js-integration-kissmetrics":125,"analytics.js-integration-klaviyo":126,"analytics.js-integration-livechat":127,"analytics.js-integration-lucky-orange":128,"analytics.js-integration-lytics":129,"analytics.js-integration-mixpanel":130,"analytics.js-integration-mojn":131,"analytics.js-integration-mouseflow":132,"analytics.js-integration-mousestats":133,"analytics.js-integration-navilytics":134,"analytics.js-integration-nudgespot":135,"analytics.js-integration-olark":136,"analytics.js-integration-optimizely":137,"analytics.js-integration-outbound":138,"analytics.js-integration-perfect-audience":139,"analytics.js-integration-pingdom":140,"analytics.js-integration-piwik":141,"analytics.js-integration-preact":142,"analytics.js-integration-qualaroo":143,"analytics.js-integration-quantcast":144,"analytics.js-integration-rollbar":145,"analytics.js-integration-route":146,"analytics.js-integration-saasquatch":147,"analytics.js-integration-satismeter":148,"analytics.js-integration-segmentio":149,"analytics.js-integration-sentry":150,"analytics.js-integration-snapengage":151,"analytics.js-integration-spinnakr":152,"analytics.js-integration-supporthero":153,"analytics.js-integration-taplytics":154,"analytics.js-integration-tapstream":155,"analytics.js-integration-trakio":156,"analytics.js-integration-twitter-ads":157,"analytics.js-integration-userlike":158,"analytics.js-integration-uservoice":159,"analytics.js-integration-vero":160,"analytics.js-integration-visual-website-optimizer":161,"analytics.js-integration-webengage":162,"analytics.js-integration-woopra":163,"analytics.js-integration-wootric":164,"analytics.js-integration-yandex-metrica":165}], -79: [function(require, module, exports) { - -/** - * Module dependencies. - */ - -var integration = require('analytics.js-integration'); -var snake = require('to-snake-case'); -var useHttps = require('use-https'); -var each = require('each'); -var is = require('is'); -var del = require('obj-case').del; - -/** - * Expose `AdRoll` integration. - */ - -var AdRoll = module.exports = integration('AdRoll') - .assumesPageview() - .global('__adroll') - .global('__adroll_loaded') - .global('adroll_adv_id') - .global('adroll_custom_data') - .global('adroll_pix_id') - .option('advId', '') - .option('pixId', '') - .tag('http', ''); - -/** - * Initialize Chameleon. - * - * @api public - */ - -Chameleon.prototype.initialize = function() { - /* eslint-disable */ - (window.chmln={}),names='setup alias track set'.split(' ');for (var i=0;i'); - -/** - * Initialize. - * - * http://chartbeat.com/docs/configuration_variables/ - * - * @api public - */ - -Chartbeat.prototype.initialize = function() { - var self = this; - - window._sf_async_config = window._sf_async_config || {}; - window._sf_async_config.useCanonical = true; - defaults(window._sf_async_config, this.options); - - onBody(function() { - window._sf_endpt = new Date().getTime(); - // Note: Chartbeat depends on document.body existing so the script does - // not load until that is confirmed. Otherwise it may trigger errors. - self.load(self.ready); - }); -}; - -/** - * Loaded? - * - * @api private - * @return {boolean} - */ - -Chartbeat.prototype.loaded = function() { - return !!window.pSUPERFLY; -}; - -/** - * Page. - * - * http://chartbeat.com/docs/handling_virtual_page_changes/ - * - * @api public - * @param {Page} page - */ - -Chartbeat.prototype.page = function(page) { - var category = page.category(); - if (category) window._sf_async_config.sections = category; - var author = page.proxy('properties.author'); - if (author) window._sf_async_config.authors = author; - var props = page.properties(); - var name = page.fullName(); - window.pSUPERFLY.virtualPage(props.path, name || props.title); -}; - -}, {"defaults":192,"analytics.js-integration":166,"on-body":193}], -192: [function(require, module, exports) { -/** - * Expose `defaults`. - */ -module.exports = defaults; - -function defaults (dest, defaults) { - for (var prop in defaults) { - if (! (prop in dest)) { - dest[prop] = defaults[prop]; - } - } - - return dest; -}; - -}, {}], -193: [function(require, module, exports) { -var each = require('each'); - - -/** - * Cache whether `` exists. - */ - -var body = false; - - -/** - * Callbacks to call when the body exists. - */ - -var callbacks = []; - - -/** - * Export a way to add handlers to be invoked once the body exists. - * - * @param {Function} callback A function to call when the body exists. - */ - -module.exports = function onBody (callback) { - if (body) { - call(callback); - } else { - callbacks.push(callback); - } -}; - - -/** - * Set an interval to check for `document.body`. - */ - -var interval = setInterval(function () { - if (!document.body) return; - body = true; - each(callbacks, call); - clearInterval(interval); -}, 5); - - -/** - * Call a callback, passing it the body. - * - * @param {Function} callback The callback to call. - */ - -function call (callback) { - callback(document.body); -} -}, {"each":177}], -94: [function(require, module, exports) { - -/** - * Module dependencies. - */ - -var date = require('load-date'); -var domify = require('domify'); -var each = require('each'); -var integration = require('analytics.js-integration'); -var is = require('is'); -var onBody = require('on-body'); -var useHttps = require('use-https'); - -/** - * Expose `ClickTale` integration. - */ - -var ClickTale = module.exports = integration('ClickTale') - .assumesPageview() - .global('WRInitTime') - .global('ClickTale') - .global('ClickTaleSetUID') - .global('ClickTaleField') - .global('ClickTaleEvent') - .option('httpCdnUrl', 'http://s.clicktale.net/WRe0.js') - .option('httpsCdnUrl', '') - .option('projectId', '') - .option('recordingRatio', 0.01) - .option('partitionId', '') - .tag(''); - -/** - * Initialize. - * - * http://clicky.com/help/customization - * - * @api public - */ - -Clicky.prototype.initialize = function() { - var user = this.analytics.user(); - window.clicky_site_ids = window.clicky_site_ids || [this.options.siteId]; - this.identify(new Identify({ - userId: user.id(), - traits: user.traits() - })); - this.load(this.ready); -}; - -/** - * Loaded? - * - * @api private - * @return {boolean} - */ - -Clicky.prototype.loaded = function() { - return is.object(window.clicky); -}; - -/** - * Page. - * - * http://clicky.com/help/customization#/help/custom/manual - * - * @api public - * @param {Page} page - */ - -Clicky.prototype.page = function(page) { - var properties = page.properties(); - var name = page.fullName(); - window.clicky.log(properties.path, name || properties.title); -}; - -/** - * Identify. - * - * @api public - * @param {Identify} [id] - */ - -Clicky.prototype.identify = function(identify) { - window.clicky_custom = window.clicky_custom || {}; - window.clicky_custom.session = window.clicky_custom.session || {}; - var traits = identify.traits(); - - var username = identify.username(); - var email = identify.email(); - var name = identify.name(); - - if (username || email || name) traits.username = username || email || name; - - extend(window.clicky_custom.session, traits); -}; - -/** - * Track. - * - * http://clicky.com/help/customization#/help/custom/manual - * - * @api public - * @param {Track} event - */ - -Clicky.prototype.track = function(track) { - window.clicky.goal(track.event(), track.revenue()); -}; - -}, {"facade":9,"extend":68,"analytics.js-integration":166,"is":18}], -96: [function(require, module, exports) { - -/** - * Module dependencies. - */ - -var integration = require('analytics.js-integration'); -var useHttps = require('use-https'); - -/** - * Expose `Comscore` integration. - */ - -var Comscore = module.exports = integration('comScore') - .assumesPageview() - .global('_comscore') - .global('COMSCORE') - .option('c1', '2') - .option('c2', '') - .tag('http', ''); -}; - -}, {"bind":55,"domify":186,"each":4,"extend":68,"analytics.js-integration":166,"json":59}], -105: [function(require, module, exports) { - -/** - * Module dependencies. - */ - -var each = require('each'); -var integration = require('analytics.js-integration'); -var push = require('global-queue')('_fbq'); - -/** - * Expose `Facebook` - */ - -var Facebook = module.exports = integration('Facebook Conversion Tracking') - .global('_fbq') - .option('currency', 'USD') - .tag(''); - -/** - * Initialize. - */ - -FullStory.prototype.initialize = function() { - var self = this; - window._fs_debug = this.options.debug; - window._fs_host = 'www.fullstory.com'; - window._fs_org = this.options.org; - - /* eslint-disable */ - (function(m,n,e,t,l,o,g,y){ - g=m[e]=function(a,b){g.q?g.q.push([a,b]):g._api(a,b);};g.q=[]; - g.identify=function(i,v){g(l,{uid:i});if(v)g(l,v)};g.setUserVars=function(v){FS(l,v)}; - g.setSessionVars=function(v){FS('session',v)};g.setPageVars=function(v){FS('page',v)}; - self.ready(); - self.load(); - })(window,document,'FS','script','user'); - /* eslint-enable */ -}; - -/** - * Loaded? - * - * @return {Boolean} - */ - -FullStory.prototype.loaded = function() { - return !!window.FS; -}; - -/** - * Identify. - * - * @param {Identify} identify - */ - -FullStory.prototype.identify = function(identify) { - var id = identify.userId() || identify.anonymousId(); - var traits = identify.traits({ name: 'displayName' }); - - var newTraits = foldl(function(results, value, key) { - if (key !== 'id') results[key === 'displayName' || key === 'email' ? key : convert(key, value)] = value; - return results; - }, {}, traits); - - window.FS.identify(String(id), newTraits); -}; - -/** -* Convert to FullStory format. -* -* @param {string} trait -* @param {*} value -*/ - -function convert(key, value) { - key = camel(key); - if (is.string(value)) return key + '_str'; - if (isInt(value)) return key + '_int'; - if (isFloat(value)) return key + '_real'; - if (is.date(value)) return key + '_date'; - if (is.boolean(value)) return key + '_bool'; -} - -/** - * Check if n is a float. - */ - -function isFloat(n) { - return n === +n && n !== (n | 0); -} - -/** - * Check if n is an integer. - */ - -function isInt(n) { - return n === +n && n === (n | 0); -} - -}, {"to-camel-case":203,"foldl":180,"analytics.js-integration":166,"is":18}], -203: [function(require, module, exports) { - -var toSpace = require('to-space-case'); - - -/** - * Expose `toCamelCase`. - */ - -module.exports = toCamelCase; - - -/** - * Convert a `string` to camel case. - * - * @param {String} string - * @return {String} - */ - - -function toCamelCase (string) { - return toSpace(string).replace(/\s(\w)/g, function (matches, letter) { - return letter.toUpperCase(); - }); -} -}, {"to-space-case":187}], -110: [function(require, module, exports) { - -/** - * Module dependencies. - */ - -var integration = require('analytics.js-integration'); -var push = require('global-queue')('_gauges'); - -/** - * Expose `Gauges` integration. - */ - -var Gauges = module.exports = integration('Gauges') - .assumesPageview() - .global('_gauges') - .option('siteId', '') - .tag('') - .tag('pixel', ''); - -/** - * Initialize Wootric. - * - * @api public - */ - -Wootric.prototype.initialize = function() { - // We use this to keep track of the last page that Wootric has tracked to - // ensure we don't accidentally send a duplicate page call - this.lastPageTracked = null; - window.wootricSettings = window.wootricSettings || {}; - window.wootricSettings.account_token = this.options.accountToken; - - var self = this; - this.load('library', function() { - self.ready(); - }); -}; - -/** - * Has the Wootric library been loaded yet? - * - * @api private - * @return {boolean} - */ - -Wootric.prototype.loaded = function() { - // We are always ready since we are just setting a global variable in initialize - return !!window.wootric; -}; - -/** - * Identify a user. - * - * @api public - * @param {Facade} identify - */ - -Wootric.prototype.identify = function(identify) { - var traits = identify.traits(); - var email = identify.email(); - var createdAt = identify.created(); - var language = traits.language; - - if (createdAt && createdAt.getTime) window.wootricSettings.created_at = createdAt.getTime(); - if (language) window.wootricSettings.language = language; - window.wootricSettings.email = email; - // Set the rest of the traits as properties - window.wootricSettings.properties = omit(['created', 'createdAt', 'email'], traits); - - window.wootric('run'); -}; - -/** - * Page. - * - * @api public - * @param {Page} page - */ - -Wootric.prototype.page = function(page) { - // Only track page if we haven't already tracked it - if (this.lastPageTracked === window.location) { - return; - } - - // Set this page as the last page tracked - this.lastPageTracked = window.location; - - var wootricSettings = window.wootricSettings; - this.load('pixel', { - accountToken: this.options.accountToken, - email: encodeURIComponent(wootricSettings.email), - createdAt: wootricSettings.created_at, - url: encodeURIComponent(page.url()), - cacheBuster: Math.random() - }); -}; - -}, {"analytics.js-integration":166,"omit":205}], -165: [function(require, module, exports) { - -/** - * Module dependencies. - */ - -var bind = require('bind'); -var integration = require('analytics.js-integration'); -var tick = require('next-tick'); -var when = require('when'); - -/** - * Expose `Yandex` integration. - */ - -var Yandex = module.exports = integration('Yandex Metrica') - .assumesPageview() - .global('yandex_metrika_callbacks') - .global('Ya') - .option('counterId', null) - .option('clickmap', false) - .option('webvisor', false) - .tag('');Chameleon.prototype.initialize=function(){window.chmln={},names="setup alias track set".split(" ");for(var i=0;i');Chartbeat.prototype.initialize=function(){var self=this;window._sf_async_config=window._sf_async_config||{};window._sf_async_config.useCanonical=true;defaults(window._sf_async_config,this.options);onBody(function(){window._sf_endpt=(new Date).getTime();self.load(self.ready)})};Chartbeat.prototype.loaded=function(){return!!window.pSUPERFLY};Chartbeat.prototype.page=function(page){var category=page.category();if(category)window._sf_async_config.sections=category;var author=page.proxy("properties.author");if(author)window._sf_async_config.authors=author;var props=page.properties();var name=page.fullName();window.pSUPERFLY.virtualPage(props.path,name||props.title)}},{defaults:192,"analytics.js-integration":166,"on-body":193}],192:[function(require,module,exports){module.exports=defaults;function defaults(dest,defaults){for(var prop in defaults){if(!(prop in dest)){dest[prop]=defaults[prop]}}return dest}},{}],193:[function(require,module,exports){var each=require("each");var body=false;var callbacks=[];module.exports=function onBody(callback){if(body){call(callback)}else{callbacks.push(callback)}};var interval=setInterval(function(){if(!document.body)return;body=true;each(callbacks,call);clearInterval(interval)},5);function call(callback){callback(document.body)}},{each:177}],94:[function(require,module,exports){var date=require("load-date");var domify=require("domify");var each=require("each");var integration=require("analytics.js-integration");var is=require("is");var onBody=require("on-body");var useHttps=require("use-https");var ClickTale=module.exports=integration("ClickTale").assumesPageview().global("WRInitTime").global("ClickTale").global("ClickTaleSetUID").global("ClickTaleField").global("ClickTaleEvent").option("httpCdnUrl","http://s.clicktale.net/WRe0.js").option("httpsCdnUrl","").option("projectId","").option("recordingRatio",.01).option("partitionId","").tag('');Clicky.prototype.initialize=function(){var user=this.analytics.user();window.clicky_site_ids=window.clicky_site_ids||[this.options.siteId];this.identify(new Identify({userId:user.id(),traits:user.traits()}));this.load(this.ready)};Clicky.prototype.loaded=function(){return is.object(window.clicky)};Clicky.prototype.page=function(page){var properties=page.properties();var name=page.fullName();window.clicky.log(properties.path,name||properties.title)};Clicky.prototype.identify=function(identify){window.clicky_custom=window.clicky_custom||{};window.clicky_custom.session=window.clicky_custom.session||{};var traits=identify.traits();var username=identify.username();var email=identify.email();var name=identify.name();if(username||email||name)traits.username=username||email||name;extend(window.clicky_custom.session,traits)};Clicky.prototype.track=function(track){window.clicky.goal(track.event(),track.revenue())}},{facade:9,extend:68,"analytics.js-integration":166,is:18}],96:[function(require,module,exports){var integration=require("analytics.js-integration");var useHttps=require("use-https");var Comscore=module.exports=integration("comScore").assumesPageview().global("_comscore").global("COMSCORE").option("c1","2").option("c2","").tag("http",'")}},{bind:55,domify:186,each:4,extend:68,"analytics.js-integration":166,json:59}],105:[function(require,module,exports){var each=require("each");var integration=require("analytics.js-integration");var push=require("global-queue")("_fbq");var Facebook=module.exports=integration("Facebook Conversion Tracking").global("_fbq").option("currency","USD").tag('');FullStory.prototype.initialize=function(){var self=this;window._fs_debug=this.options.debug;window._fs_host="www.fullstory.com";window._fs_org=this.options.org;(function(m,n,e,t,l,o,g,y){g=m[e]=function(a,b){g.q?g.q.push([a,b]):g._api(a,b)};g.q=[];g.identify=function(i,v){g(l,{uid:i});if(v)g(l,v)};g.setUserVars=function(v){FS(l,v)};g.setSessionVars=function(v){FS("session",v)};g.setPageVars=function(v){FS("page",v)};self.ready();self.load()})(window,document,"FS","script","user")};FullStory.prototype.loaded=function(){return!!window.FS};FullStory.prototype.identify=function(identify){var id=identify.userId()||identify.anonymousId();var traits=identify.traits({name:"displayName"});var newTraits=foldl(function(results,value,key){if(key!=="id")results[key==="displayName"||key==="email"?key:convert(key,value)]=value;return results},{},traits);window.FS.identify(String(id),newTraits)};function convert(key,value){key=camel(key);if(is.string(value))return key+"_str";if(isInt(value))return key+"_int";if(isFloat(value))return key+"_real";if(is.date(value))return key+"_date";if(is.boolean(value))return key+"_bool"}function isFloat(n){return n===+n&&n!==(n|0)}function isInt(n){return n===+n&&n===(n|0)}},{"to-camel-case":203,foldl:180,"analytics.js-integration":166,is:18}],203:[function(require,module,exports){var toSpace=require("to-space-case");module.exports=toCamelCase;function toCamelCase(string){return toSpace(string).replace(/\s(\w)/g,function(matches,letter){return letter.toUpperCase()})}},{"to-space-case":187}],110:[function(require,module,exports){var integration=require("analytics.js-integration");var push=require("global-queue")("_gauges");var Gauges=module.exports=integration("Gauges").assumesPageview().global("_gauges").option("siteId","").tag('').tag("pixel",'');Wootric.prototype.initialize=function(){this.lastPageTracked=null;window.wootricSettings=window.wootricSettings||{};window.wootricSettings.account_token=this.options.accountToken;var self=this;this.load("library",function(){self.ready()})};Wootric.prototype.loaded=function(){return!!window.wootric};Wootric.prototype.identify=function(identify){var traits=identify.traits();var email=identify.email();var createdAt=identify.created();var language=traits.language;if(createdAt&&createdAt.getTime)window.wootricSettings.created_at=createdAt.getTime();if(language)window.wootricSettings.language=language;window.wootricSettings.email=email;window.wootricSettings.properties=omit(["created","createdAt","email"],traits);window.wootric("run")};Wootric.prototype.page=function(page){if(this.lastPageTracked===window.location){return}this.lastPageTracked=window.location;var wootricSettings=window.wootricSettings;this.load("pixel",{accountToken:this.options.accountToken,email:encodeURIComponent(wootricSettings.email),createdAt:wootricSettings.created_at,url:encodeURIComponent(page.url()),cacheBuster:Math.random()})}},{"analytics.js-integration":166,omit:205}],165:[function(require,module,exports){var bind=require("bind");var integration=require("analytics.js-integration");var tick=require("next-tick");var when=require("when");var Yandex=module.exports=integration("Yandex Metrica").assumesPageview().global("yandex_metrika_callbacks").global("Ya").option("counterId",null).option("clickmap",false).option("webvisor",false).tag('