├── min
└── .gitkeep
├── .gitignore
├── src
├── komito
│ ├── trackers
│ │ ├── __ns__.js
│ │ ├── social
│ │ │ ├── __ns__.js
│ │ │ ├── twitter.js
│ │ │ ├── linkedin.js
│ │ │ ├── users.js
│ │ │ └── facebook.js
│ │ ├── dom
│ │ │ ├── adblock.js
│ │ │ ├── print.js
│ │ │ ├── scroll.js
│ │ │ ├── forms.js
│ │ │ ├── orientation.js
│ │ │ ├── __ns__.js
│ │ │ └── links.js
│ │ └── media
│ │ │ ├── __ns__.js
│ │ │ ├── html5.js
│ │ │ ├── vimeo.js
│ │ │ └── youtube.js
│ └── __ns__.js
└── glize
│ ├── net
│ ├── __ns__.js
│ └── HttpRequest.js
│ ├── util
│ ├── __ns__.js
│ ├── Base64.js
│ ├── String.js
│ ├── Object.js
│ ├── Date.js
│ ├── StringUtils.js
│ └── Array.js
│ ├── compressors
│ └── __ns__.js
│ └── dom
│ └── __ns__.js
├── .travis.yml
├── CONTRIBUTING.md
├── .github
├── FUNDING.yml
└── workflows
│ ├── npm-publish.yml
│ └── codeql-analysis.yml
├── npm
├── package.json
├── index.js
└── README.md
├── package.json
├── README.md
├── SECURITY.md
├── CODE_OF_CONDUCT.md
└── LICENSE
/min/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | *.svn
3 | *.svn-base
4 | *.so
5 | .DS_Store
6 | build/lib
7 | min/komito.js
8 |
--------------------------------------------------------------------------------
/src/komito/trackers/__ns__.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Defines komito.trackers namespace.
3 | * @namespace
4 | */
5 | komito.trackers = {};
6 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | # python and sudo are required for closure linter:
2 | # `sudo python setup.py install`
3 | language: python
4 | python: 2.7
5 | sudo: required
6 | script:
7 | - ./build/build.sh
8 | - ./build/npmjs.sh
9 |
--------------------------------------------------------------------------------
/src/glize/net/__ns__.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview Defines net namespace.
3 | *
4 | * @see http://google.github.io/styleguide/javascriptguide.xml
5 | * @see http://developers.google.com/closure/compiler/docs/js-for-compiler
6 | */
7 |
8 |
9 | /**
10 | * Defines net namespace.
11 | * @namespace
12 | */
13 | var net = {};
14 |
--------------------------------------------------------------------------------
/src/glize/util/__ns__.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview Defines namespace for util package.
3 | *
4 | * @see http://google.github.io/styleguide/javascriptguide.xml
5 | * @see http://developers.google.com/closure/compiler/docs/js-for-compiler
6 | */
7 |
8 |
9 | /**
10 | * Defines namespace for util package.
11 | * @namespace
12 | */
13 | var util = {};
14 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ## Contributing
2 |
3 | We use the pull-request model, see [GitHub's help on pull-request](https://help.github.com/articles/using-pull-requests).
4 |
5 | In brief, you will:
6 |
7 | - add an issue about what you plan to change;
8 | - on GitHub, find and fork the source repository;
9 | - on your computer, clone your fork repository,
10 | - commit your changes in a new branch;
11 | - push your branch and submit a pull-request for it;
12 | - go through the review process until your pull-request is merged; and
13 | - close your issue.
14 |
--------------------------------------------------------------------------------
/src/komito/trackers/social/__ns__.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Defines komito.trackers.social namespace.
3 | * @namespace
4 | */
5 | komito.trackers.social = {
6 |
7 | /**
8 | * Initializes social events tracking.
9 | */
10 | init: function() {
11 | /** @type {!Object.} */ var map = {
12 | 'trackFacebook': komito.trackers.social.Facebook,
13 | 'trackLinkedIn': komito.trackers.social.LinkedIn,
14 | 'trackTwitter': komito.trackers.social.Twitter,
15 | 'trackUsers': komito.trackers.social.Users
16 | };
17 | /** @type {string} */ var key;
18 |
19 | for (key in map) {
20 | komito.config[key] && new map[key];
21 | }
22 | }
23 | };
24 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: Datamart # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | #patreon: # Replace with a single Patreon username
5 | #open_collective: # Replace with a single Open Collective username
6 | #ko_fi: # Replace with a single Ko-fi username
7 | #tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | #community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | #liberapay: # Replace with a single Liberapay username
10 | #issuehunt: # Replace with a single IssueHunt username
11 | #otechie: # Replace with a single Otechie username
12 | # custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
13 | custom: ['https://www.paypal.me/vpodk', 'https://amzn.to/3mpgAJh']
14 |
--------------------------------------------------------------------------------
/npm/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "komito-analytics",
3 | "version": "1.0.6",
4 | "description": "Komito Analytics is an enhancement for the most popular analytics software.",
5 | "homepage": "https://komito.net/",
6 | "repository": "github:Datamart/Komito",
7 | "license": "Apache-2.0",
8 | "bugs": {
9 | "url": "https://github.com/Datamart/Komito/issues",
10 | "email": "support@komito.net"
11 | },
12 | "author": {
13 | "name": "Valentin Podkamennyi",
14 | "email": "valentin@dtm.io",
15 | "url": "https://vpodk.com/"
16 | },
17 | "keywords": [
18 | "adobe-analytics",
19 | "analytics",
20 | "baidu-analytics",
21 | "event-tracking",
22 | "google-analytics",
23 | "komito",
24 | "komito-analytics",
25 | "media-tracking",
26 | "particles-analytics"
27 | ],
28 | "main": "index.js"
29 | }
30 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "komito-analytics",
3 | "version": "5.0.0",
4 | "description": "Komito Analytics is a free, open-source enhancement for the most popular web analytics software.",
5 | "homepage": "https://komito.net",
6 | "repository": "github:Datamart/Komito",
7 | "license": "Apache-2.0",
8 | "bugs": {
9 | "url": "https://github.com/Datamart/Komito/issues",
10 | "email": "support@komito.net"
11 | },
12 | "scripts": {
13 | "build": "./build/build.sh"
14 | },
15 | "author": {
16 | "name": "Valentin Podkamennyi",
17 | "email": "valentin@dtm.io",
18 | "url": "https://vpodk.com/"
19 | },
20 | "dependencies": {
21 | "glize": "^21.2.2"
22 | },
23 | "keywords": [
24 | "analytics",
25 | "google-analytics",
26 | "event-tracking",
27 | "adobe-analytics",
28 | "clicktale",
29 | "baidu-analytics",
30 | "media-tracking",
31 | "komito-analytics",
32 | "particles-analytics"
33 | ]
34 | }
35 |
--------------------------------------------------------------------------------
/src/komito/trackers/dom/adblock.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview Tracks pageviews with blocked ads.
3 | */
4 |
5 |
6 |
7 | /**
8 | * Defines komito.trackers.dom.AdBlock constructor.
9 | * @constructor
10 | */
11 | komito.trackers.dom.AdBlock = function() {
12 |
13 | /**
14 | * Initializes orientation tracking.
15 | * @private
16 | */
17 | function init_() {
18 | if (komito.config['trackAdblock']) {
19 | var node = dom.appendChild(dom.document.body, dom.createElement('DIV'));
20 | node.id = 'ad-container';
21 | node.style.position = 'absolute';
22 | node.innerHTML = '
';
24 |
25 | setTimeout(function() {
26 | node.offsetHeight < 5 && komito.track(
27 | komito.EVENT_ACTION_TYPE, 'adblock', 'pageview', location.href);
28 | dom.removeNode(node);
29 | }, 1E3);
30 | }
31 | }
32 |
33 | // Initializing adblock tracking.
34 | init_();
35 | };
36 |
--------------------------------------------------------------------------------
/src/komito/trackers/dom/print.js:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | /**
5 | * Defines komito.trackers.dom.print constructor.
6 | * @constructor
7 | */
8 | komito.trackers.dom.Print = function() {
9 |
10 | /**
11 | * Initializes print tracking.
12 | * @private
13 | */
14 | function init_() {
15 | if (komito.config['trackPrint']) {
16 | /** @type {function(string): ?MediaQueryList} */
17 | var matchMedia = dom.context['matchMedia'];
18 | mql_ = matchMedia && matchMedia('print');
19 |
20 | mql_ ?
21 | mql_['addListener'](listener_) :
22 | dom.events.addEventListener(dom.context, 'afterprint', listener_);
23 | }
24 | }
25 |
26 | /**
27 | * @param {?Event} e The print event.
28 | * @private
29 | */
30 | function listener_(e) {
31 | komito.track(
32 | komito.EVENT_ACTION_TYPE, 'print',
33 | dom.document.title, location.href);
34 | mql_ ?
35 | mql_['removeListener'](listener_) :
36 | dom.events.removeEventListener(dom.context, 'afterprint', listener_);
37 | mql_ = listener_ = dom.NULL;
38 | }
39 |
40 | /**
41 | * @type {?MediaQueryList}
42 | * @private
43 | */
44 | var mql_;
45 |
46 | // Initializing print events tracking.
47 | init_();
48 | };
49 |
--------------------------------------------------------------------------------
/npm/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview Komito Analytics is an enhancement for the most popular
3 | * analytics software. It unlocks power of digital analytics with additional
4 | * insights about visitor's behavior.
5 | *
6 | * @see http://google.github.io/styleguide/javascriptguide.xml
7 | * @see http://developers.google.com/closure/compiler/docs/js-for-compiler
8 | * @see https://github.com/Datamart/Komito/
9 | */
10 |
11 |
12 |
13 | module.exports = {
14 | /**
15 | * Initializes Komito Analytics extension.
16 | * @param {!Object=} opt_options Optional tracking options.
17 | */
18 | init: (opt_options) => {
19 | const root = ('object' === typeof self && self.self === self && self) ||
20 | ('object' === typeof global && global.global === global && global);
21 | const doc = root.document;
22 |
23 | if (doc) {
24 | root['_komito'] = root['_komito'] || {};
25 |
26 | if (opt_options) {
27 | for (let key in opt_options) {
28 | if (opt_options.hasOwnProperty(key)) {
29 | root['_komito'][key] = opt_options[key];
30 | }
31 | }
32 | }
33 |
34 | const script = doc.createElement('SCRIPT');
35 | const scripts = doc.getElementsByTagName('SCRIPT');
36 | const parent = scripts[scripts.length - 1].parentNode;
37 |
38 | script.async = true;
39 | script.src = 'https://komito.net/komito.js';
40 | (parent || doc.body).appendChild(script);
41 | }
42 | }
43 | };
44 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Komito Analytics [](https://twitter.com/intent/tweet?text=Komito%20Analytics%20-%20Unlock%20the%20power%20of%20digital%20analytics%20with%20additional%20insights%20about%20visitor%27s%20behavior.&url=https://komito.net/&via=GitHub&hashtags=KomitoAnalytics,GoogleAnalytics,AdobeAnalytics,EventTracking,MediaTracking)
2 | [](http://www.apache.org/licenses/LICENSE-2.0.html) [](https://komito.net) [](https://npmjs.org/package/komito-analytics) [](https://npmjs.org/package/komito-analytics) [](https://openbase.com/js/komito-analytics?utm_source=embedded&utm_medium=badge&utm_campaign=rate-badge)
3 |
4 | Komito Analytics is a free, open-source enhancement for the most popular web analytics software.
5 | It unlocks power of digital analytics with additional insights about visitor's behavior.
6 | For more information please visit [Komito Analytics project page](https://komito.net).
7 |
8 | 
9 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policies and Procedures
2 |
3 | This document outlines security procedures and general policies for the
4 | `Komito Analytics` project.
5 |
6 | * [Reporting a Bug](#reporting-a-bug)
7 | * [Disclosure Policy](#disclosure-policy)
8 | * [Comments on this Policy](#comments-on-this-policy)
9 |
10 | ## Reporting a Bug
11 |
12 | Thank you for improving the security of `Komito Analytics`. We appreciate your
13 | efforts and responsible disclosure and will make every effort to acknowledge
14 | your contributions.
15 |
16 | Report security bugs by emailing the lead maintainer at support@komito.net.
17 |
18 | The lead maintainer will acknowledge your email within 48 hours, and will send a
19 | more detailed response within 48 hours indicating the next steps in handling
20 | your report. After the initial reply to your report, our team will
21 | endeavor to keep you informed of the progress towards a fix and full
22 | announcement, and may ask for additional information or guidance.
23 |
24 | ## Disclosure Policy
25 |
26 | When our team receives a security bug report, they will assign it to a
27 | primary handler. This person will coordinate the fix and release process,
28 | involving the following steps:
29 |
30 | * Confirm the problem and determine the affected versions.
31 | * Audit code to find any potential similar problems.
32 | * Prepare fixes for all releases still under maintenance.
33 |
34 | ## Comments on this Policy
35 |
36 | If you have suggestions on how this process could be improved please submit a
37 | pull request.
38 |
--------------------------------------------------------------------------------
/.github/workflows/npm-publish.yml:
--------------------------------------------------------------------------------
1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages
3 |
4 | name: Publish NPM Package
5 |
6 | on:
7 | release:
8 | types: [created]
9 |
10 | defaults:
11 | run:
12 | working-directory: ./npm
13 |
14 | jobs:
15 | build:
16 | runs-on: ubuntu-latest
17 | steps:
18 | - uses: actions/checkout@v2
19 | - uses: actions/setup-node@v1
20 | with:
21 | node-version: 12
22 | - run: npm i
23 | - run: npm ci
24 | - run: npm test
25 |
26 | publish-npm:
27 | needs: build
28 | runs-on: ubuntu-latest
29 | steps:
30 | - uses: actions/checkout@v2
31 | - uses: actions/setup-node@v1
32 | with:
33 | node-version: 12
34 | registry-url: https://registry.npmjs.org/
35 | - run: npm i
36 | - run: npm ci
37 | - run: npm publish
38 | env:
39 | NODE_AUTH_TOKEN: ${{secrets.npm_token}}
40 |
41 | # publish-gpr:
42 | # needs: build
43 | # runs-on: ubuntu-latest
44 | # steps:
45 | # - uses: actions/checkout@v2
46 | # - uses: actions/setup-node@v1
47 | # with:
48 | # node-version: 12
49 | # registry-url: https://npm.pkg.github.com/
50 | # - run: npm i
51 | # - run: npm ci
52 | # - run: npm publish
53 | # env:
54 | # NODE_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}}
55 |
--------------------------------------------------------------------------------
/src/komito/trackers/media/__ns__.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview Komito Analytics namespace for Media trackers.
3 | *
4 | * @link http://google.github.io/styleguide/javascriptguide.xml
5 | * @link http://developers.google.com/closure/compiler/docs/js-for-compiler
6 | */
7 |
8 |
9 | /**
10 | * Defines komito.trackers.media namespace.
11 | * @namespace
12 | */
13 | komito.trackers.media = {
14 |
15 | /**
16 | * Initializes media tracking enabled by trackMedia parameter.
17 | *
18 | * @see komito.config
19 | * @see komito.trackers.media.HTML5
20 | * @see komito.trackers.media.Vimeo
21 | * @see komito.trackers.media.YouTube
22 | */
23 | init: function() {
24 | /** @type {!Array|!Function|number|string} */
25 | var trackMedia = komito.config['trackMedia'];
26 | /** @type {!Array.} */ var types = ['html5', 'vimeo', 'youtube'];
27 | /** @type {string} */ var type;
28 | /** @type {number} */ var length;
29 |
30 | if (trackMedia) {
31 | if (util.Array.isArray(trackMedia)) {
32 | types = /** @type {!Array.} */ (trackMedia);
33 | }
34 |
35 | length = types.length;
36 | for (; length--;) {
37 | type = types[length].toLowerCase();
38 | if ('html5' == type)
39 | komito.trackers.media.HTML5 && new komito.trackers.media.HTML5;
40 | else if ('vimeo' == type)
41 | komito.trackers.media.Vimeo && new komito.trackers.media.Vimeo;
42 | else if ('youtube' == type)
43 | komito.trackers.media.YouTube && new komito.trackers.media.YouTube;
44 | }
45 | }
46 | }
47 | };
48 |
--------------------------------------------------------------------------------
/src/komito/trackers/dom/scroll.js:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | /**
5 | * Defines komito.trackers.dom.scroll constructor.
6 | * @constructor
7 | */
8 | komito.trackers.dom.Scroll = function() {
9 |
10 | /**
11 | * Initializes scroll tracking.
12 | * @private
13 | */
14 | function init_() {
15 | /** @type {!Array|!Function|number|string} */
16 | var trackScroll = komito.config['trackScroll'];
17 | if (trackScroll) {
18 | /** @type {!Array.} */ var percentages = [25, 50, 75, 100];
19 | if (util.Array.isArray(trackScroll)) {
20 | percentages = /** @type {!Array.} */ (trackScroll);
21 | }
22 |
23 | /** @type {?Element} */ var root = dom.document.documentElement;
24 | /** @type {?Element} */ var body = dom.document.body;
25 | /** @type {!Object} */ var map = {};
26 | /** @type {number} */ var depth = percentages.length;
27 | for (; depth--;) { map[percentages[depth]] = 0; }
28 |
29 | dom.events.addEventListener(
30 | dom.context, dom.events.TYPE.SCROLL, function() {
31 | var screenHeight = root.clientHeight || dom.context.innerHeight;
32 | var scrollHeight = root.scrollHeight || body.offsetHeight;
33 | var scrollTop = root.scrollTop || body.scrollTop;
34 | var percent = scrollTop / (scrollHeight - screenHeight) * 100;
35 | depth = ~~(percent / 25) * 25;
36 | if (depth && depth in map && !map[depth]) {
37 | map[depth] = 1;
38 | komito.track(
39 | komito.EVENT_ACTION_TYPE, 'scroll', 'depth', depth + '%');
40 | }
41 | });
42 | }
43 | }
44 |
45 | // Initializing print events tracking.
46 | init_();
47 | };
48 |
--------------------------------------------------------------------------------
/src/komito/trackers/social/twitter.js:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | /**
5 | * Defines komito.trackers.social.Twitter constructor.
6 | * @constructor
7 | */
8 | komito.trackers.social.Twitter = function() {
9 | /**
10 | * List of Twitter events to track.
11 | * @const {!Array.}
12 | * @see https://dev.twitter.com/web/javascript/events
13 | */
14 | var EVENTS = ['tweet', 'retweet', 'like', 'follow'];
15 |
16 | /**
17 | * Mapping of event's data keys.
18 | * @const {!Object.}
19 | */
20 | var DATA_KEYS = {
21 | 'follow': 'screen_name',
22 | 'retweet': 'source_tweet_id',
23 | 'like': 'tweet_id',
24 | 'tweet': 'url'
25 | };
26 |
27 | /**
28 | * Tries to attach listener to Twitter widget object if it presents on page.
29 | * @see https://dev.twitter.com/web/javascript/events
30 | * @private
31 | */
32 | function init_() {
33 | if (9 > counter_++) {
34 | if (dom.context['twttr'] && dom.context['twttr']['ready']) {
35 | dom.context['twttr']['ready'](function(twttr) {
36 | var config = komito.config['trackTwitter'];
37 | var type;
38 | var data;
39 | var key;
40 |
41 | var events = /** @type {!Array.} */ (
42 | util.Array.isArray(config) ? config : EVENTS);
43 |
44 | util.Array.forEach(events, function(event) {
45 | twttr['events']['bind'](event, function(e) {
46 | type = e['type'];
47 | if (!fired_[type]) {
48 | fired_[type] = 1;
49 | key = DATA_KEYS[type];
50 | data = (key && e['data'] && e['data'][key]) || location.href;
51 | komito.track(komito.SOCIAL_ACTION_TYPE, 'Twitter', type, data);
52 | }
53 | });
54 | });
55 | });
56 | } else setTimeout(init_, 5e3);
57 | }
58 | }
59 |
60 |
61 | /**
62 | * @type {number}
63 | * @private
64 | */
65 | var counter_ = 0;
66 |
67 | /**
68 | * @type {!Object.}
69 | * @private
70 | */
71 | var fired_ = {};
72 |
73 | // Initializing Twitter events tracking.
74 | init_();
75 | };
76 |
--------------------------------------------------------------------------------
/src/komito/trackers/dom/forms.js:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | /**
5 | * Defines komito.trackers.dom.forms constructor.
6 | * @constructor
7 | */
8 | komito.trackers.dom.Forms = function() {
9 |
10 | /**
11 | * Initializes forms tracking.
12 | * @private
13 | */
14 | function init_() {
15 | if (komito.config['trackForms']) {
16 | /** @type {?HTMLCollection} */ var forms = dom.document.forms;
17 | /** @type {number} */ var length = forms.length;
18 | /** @type {!Element} */ var element;
19 |
20 | for (; length;) {
21 | element = forms[--length];
22 | if (!komito.DynamicContentTracker.isRegistered(element)) {
23 | komito.DynamicContentTracker.register(element);
24 |
25 | dom.events.addEventListener(
26 | element, dom.events.TYPE.SUBMIT, listener_);
27 | }
28 | }
29 | }
30 |
31 | komito.DynamicContentTracker.track(init_);
32 | }
33 |
34 | /**
35 | * @param {?Event} e The form submit event.
36 | * @private
37 | */
38 | function listener_(e) {
39 | /** @type {!HTMLFormElement} */
40 | var form = /** @type {!HTMLFormElement} */ (dom.events.getEventTarget(e));
41 |
42 | /** @type {!HTMLCollection} */ var elements = form.elements;
43 | /** @type {number} */ var length = elements.length;
44 | /** @type {number} */ var i = 0;
45 |
46 | /** @type {string} */ var action = form.getAttribute('action');
47 | /** @type {string} */ var identifier = form.getAttribute('name') ||
48 | form.getAttribute('id') ||
49 | form.className.replace(/\W+/g, '-') ||
50 | (action && util.StringUtils.hash(action)) ||
51 | ('form-' + ++index_);
52 |
53 | /** @type {!Element} */ var element;
54 |
55 | for (; i < length;) {
56 | element = elements[i++];
57 | element.name && komito.track(
58 | komito.EVENT_ACTION_TYPE, 'form', identifier,
59 | element.name + ':' + (element.type || element.tagName));
60 | }
61 |
62 | dom.events.removeEventListener(form, dom.events.TYPE.SUBMIT, listener_);
63 | }
64 |
65 | /**
66 | * The last submitted form index.
67 | * @type {number}
68 | * @private
69 | */
70 | var index_ = 0;
71 |
72 | // Initializing forms events tracking.
73 | init_();
74 | };
75 |
--------------------------------------------------------------------------------
/src/glize/util/Base64.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview Base64 utility methods.
3 | *
4 | * @see https://en.wikipedia.org/wiki/Base64
5 | * @see http://google.github.io/styleguide/javascriptguide.xml
6 | * @see http://developers.google.com/closure/compiler/docs/js-for-compiler
7 | */
8 |
9 |
10 | /**
11 | * Base64 utility methods.
12 | * @namespace
13 | * @see https://en.wikipedia.org/wiki/Base64
14 | */
15 | util.Base64 = {
16 | /**
17 | * @type {string}
18 | * @const
19 | */
20 | BASE64_CHARACTER_TABLE:
21 | 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
22 |
23 | /**
24 | * Encodes string to base64.
25 | * @param {string} str String to encode.
26 | * @return {string} Returns encoded string.
27 | */
28 | encode: function(str) {
29 | /** @type {string} */
30 | var result = dom.context.btoa ? dom.context.btoa(str) : '';
31 |
32 | if (!result) {
33 | /** @type {!Array.} */
34 | var table = util.Base64.BASE64_CHARACTER_TABLE.split('');
35 | /** @type {!Array.} */ var buffer = str.split('');
36 | /** @type {number} */ var block = 0;
37 | /** @type {number} */ var index = 0;
38 |
39 | for (; buffer[index | 0] || (table = ['='], index % 1);
40 | result += table[63 & block >> 8 - index % 1 * 8]) {
41 | block = block << 8 | str.charCodeAt(index -= -3 / 4);
42 | }
43 | }
44 |
45 | return result;
46 | },
47 |
48 | /**
49 | * Decodes base64-encoded string.
50 | * @param {string} str Encoded string.
51 | * @return {string} Returns decoded string.
52 | */
53 | decode: function(str) {
54 | /** @type {string} */
55 | var result = dom.context.atob ? dom.context.atob(str) : '';
56 |
57 | if (!result) {
58 | /** @type {!Array.} */ var buffer = str.split('');
59 | /** @type {number} */ var bit = 0;
60 | /** @type {number} */ var counter = 0;
61 | /** @type {number} */ var index = 0;
62 | /** @type {string} */ var character = '';
63 | /** @type {number} */ var i = 0;
64 |
65 | for (; character = buffer[i++];) {
66 | index = util.Base64.BASE64_CHARACTER_TABLE.indexOf(character);
67 | if (~index) {
68 | bit = counter % 4 ? bit * 64 + index : index;
69 | if (counter++ % 4) {
70 | result += String.fromCharCode(255 & bit >> (-2 * counter & 6));
71 | }
72 | }
73 | }
74 | }
75 |
76 | return result;
77 | }
78 | };
79 |
--------------------------------------------------------------------------------
/src/komito/trackers/social/linkedin.js:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | /**
5 | * Defines komito.trackers.social.LinkedIn constructor.
6 | * @constructor
7 | */
8 | komito.trackers.social.LinkedIn = function() {
9 | /**
10 | * Tries to attach listener to LinkedIn plugin if it presents on page.
11 | * @see https://developer.linkedin.com/plugins
12 | * @private
13 | */
14 | function init_() {
15 | /** @type {!Array|?NodeList} */
16 | var elements = dom.getElementsByTagName(dom.document, 'SCRIPT');
17 | /** @type {number} */ var length = elements.length;
18 | /** @type {number} */ var i = 0;
19 | /** @type {!Element} */ var element;
20 | /** @type {string} */ var type;
21 |
22 | for (; i < length;) {
23 | element = elements[i++];
24 | type = (element.getAttribute('type') || '').toLowerCase();
25 | // type: IN/Share+init, IN/FollowCompany+init
26 | type.indexOf('in/') || subscribe_(element, type.substr(3).split('+')[0]);
27 | }
28 |
29 | komito.DynamicContentTracker.track(init_);
30 | }
31 |
32 | /**
33 | * @param {!Element} element The script element.
34 | * @param {string} action The social action type.
35 | * @private
36 | */
37 | function subscribe_(element, action) {
38 | if (!komito.DynamicContentTracker.isRegistered(element)) {
39 | komito.DynamicContentTracker.register(element);
40 |
41 | /** @type {string} */ var type = 'onsuccess';
42 | /** @type {string} */ var cb = ['cb', type, action, +new Date].join('_');
43 | /** @type {?Node} */ var widget;
44 |
45 | function handler() {
46 | if (!fired_[action]) {
47 | fired_[action] = 1;
48 | komito.track(
49 | komito.SOCIAL_ACTION_TYPE, 'LinkedIn', action, location.href);
50 | widget && dom.events.removeEventListener(
51 | widget, dom.events.TYPE.CLICK, handler);
52 | }
53 | }
54 |
55 | element[type] = (element[type] ? element[type] + ',' : '') + cb;
56 | element.setAttribute('data-' + type, cb);
57 |
58 | dom.context[cb] = handler;
59 |
60 | setTimeout(function() {
61 | widget = element.previousSibling;
62 | if (widget && 'IN-widget' === widget.className) {
63 | dom.events.addEventListener(widget, dom.events.TYPE.CLICK, handler);
64 | }
65 | }, 1E3);
66 | }
67 | }
68 |
69 | /**
70 | * @type {!Object.}
71 | * @private
72 | */
73 | var fired_ = {};
74 |
75 | // Initializing LinkedIn events tracking.
76 | init_();
77 | };
78 |
--------------------------------------------------------------------------------
/src/komito/trackers/dom/orientation.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview The orientation tracking plug-in.
3 | *
4 | * @see https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia
5 | * @see https://developer.mozilla.org/en-US/docs/Web/API/Screen/orientation
6 | * @see https://developer.mozilla.org/en-US/docs/Web/Events/orientationchange
7 | */
8 |
9 |
10 |
11 | /**
12 | * Defines komito.trackers.dom.Orientation constructor.
13 | * @constructor
14 | */
15 | komito.trackers.dom.Orientation = function() {
16 | /** @const {string} */ var EVENT = 'orientationchange';
17 | /** @const {string} */ var QUERY = '(orientation: portrait)';
18 |
19 | /**
20 | * Initializes orientation tracking.
21 | * @private
22 | */
23 | function init_() {
24 | var mobile = dom.device.maxTouchPoints || 'ontouchstart' in dom.document;
25 |
26 | if (mobile && komito.config['trackOrientation']) {
27 | /** @type {function(string): ?MediaQueryList} */
28 | var matchMedia = dom.context['matchMedia'];
29 | mql_ = matchMedia && matchMedia(QUERY);
30 |
31 | komito.track(
32 | komito.EVENT_ACTION_TYPE, 'orientation', 'initial', getType_(mql_));
33 |
34 | mql_ ?
35 | mql_['addListener'](listener_) :
36 | dom.events.addEventListener(dom.context, EVENT, listener_);
37 | }
38 | }
39 |
40 | /**
41 | * @param {?Event} e The orientation change event.
42 | * @private
43 | */
44 | function listener_(e) {
45 | komito.track(
46 | komito.EVENT_ACTION_TYPE, 'orientation', 'change', getType_(e));
47 |
48 | // mql_ ?
49 | // mql_['removeListener'](listener_) :
50 | // dom.events.removeEventListener(dom.context, EVENT, listener_);
51 |
52 | // mql_ = listener_ = dom.NULL;
53 | }
54 |
55 | /**
56 | * @param {?Event|?MediaQueryList} e The orientation event.
57 | * @return {string} Return current orientation type.
58 | * @see http://developer.mozilla.org/en-US/docs/Web/API/ScreenOrientation/type
59 | * @private
60 | */
61 | function getType_(e) {
62 | /** @type {?ScreenOrientation} */ var orientation = screen['orientation'] ||
63 | screen['mozOrientation'] ||
64 | screen['msOrientation'];
65 | /** @type {string} */ var type = orientation ? orientation['type'] :
66 | (e['matches'] ? 'portrait' : 'landscape');
67 |
68 | return type.split('-')[0];
69 | }
70 |
71 | /**
72 | * @type {?MediaQueryList}
73 | * @private
74 | */
75 | var mql_;
76 |
77 | // Initializing orientation events tracking.
78 | init_();
79 | };
80 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | # For most projects, this workflow file will not need changing; you simply need
2 | # to commit it to your repository.
3 | #
4 | # You may wish to alter this file to override the set of languages analyzed,
5 | # or to provide custom queries or build logic.
6 | #
7 | # ******** NOTE ********
8 | # We have attempted to detect the languages in your repository. Please check
9 | # the `language` matrix defined below to confirm you have the correct set of
10 | # supported CodeQL languages.
11 | #
12 | name: "analyze"
13 |
14 | on:
15 | push:
16 | branches: [ master ]
17 | pull_request:
18 | # The branches below must be a subset of the branches above
19 | branches: [ master ]
20 | schedule:
21 | - cron: '45 18 * * 6'
22 |
23 | jobs:
24 | analyze:
25 | name: Analyze
26 | runs-on: ubuntu-latest
27 | permissions:
28 | actions: read
29 | contents: read
30 | security-events: write
31 |
32 | strategy:
33 | fail-fast: false
34 | matrix:
35 | language: [ 'javascript' ]
36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
37 | # Learn more:
38 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
39 |
40 | steps:
41 | - name: Checkout repository
42 | uses: actions/checkout@v2
43 |
44 | # Initializes the CodeQL tools for scanning.
45 | - name: Initialize CodeQL
46 | uses: github/codeql-action/init@v1
47 | with:
48 | languages: ${{ matrix.language }}
49 | # If you wish to specify custom queries, you can do so here or in a config file.
50 | # By default, queries listed here will override any specified in a config file.
51 | # Prefix the list here with "+" to use these queries and those in the config file.
52 | # queries: ./path/to/local/query, your-org/your-repo/queries@main
53 |
54 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
55 | # If this step fails, then you should remove it and run the build manually (see below)
56 | - name: Autobuild
57 | uses: github/codeql-action/autobuild@v1
58 |
59 | # ℹ️ Command-line programs to run using the OS shell.
60 | # 📚 https://git.io/JvXDl
61 |
62 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
63 | # and modify them (or add more) to build your code if your project
64 | # uses a compiled language
65 |
66 | #- run: |
67 | # make bootstrap
68 | # make release
69 |
70 | - name: Perform CodeQL Analysis
71 | uses: github/codeql-action/analyze@v1
72 |
--------------------------------------------------------------------------------
/npm/README.md:
--------------------------------------------------------------------------------
1 | # Komito Analytics [](https://twitter.com/intent/tweet?text=Komito%20Analytics%20-%20Unlock%20the%20power%20of%20digital%20analytics%20with%20additional%20insights%20about%20visitor%27s%20behavior.&url=https://komito.net/&via=GitHub&hashtags=KomitoAnalytics,GoogleAnalytics,AdobeAnalytics,EventTracking,MediaTracking)
2 | [](http://www.apache.org/licenses/LICENSE-2.0.html) [](https://komito.net) [](https://npmjs.org/package/komito-analytics) [](https://npmjs.org/package/komito-analytics)
3 |
4 | Komito Analytics is a free, open-source enhancement for the most popular web analytics software.
5 | It unlocks power of digital analytics with additional insights about visitor's behavior.
6 | For more information please visit [Komito Analytics project page](https://komito.net).
7 |
8 | ## Install
9 |
10 | ```
11 | $ npm install komito-analytics
12 | ```
13 |
14 | ## Usage
15 |
16 | ```js
17 | const komito = require('komito-analytics');
18 |
19 | // The default configuration can be omitted and only changed properties can be included.
20 | // @see https://komito.net/integration/
21 | komito.init({
22 | 'trackTwitter': 1, // Tracks Twitter events if widget is presented on page.
23 | 'trackFacebook': 1, // Tracks Facebook events if widget is presented on page.
24 | 'trackLinkedIn': 1, // Tracks LinkedIn events if plugin is presented on page.
25 | 'trackDownloads': 1, // Tracks files download links.
26 | 'trackOutbound': 1, // Tracks outbound links.
27 | 'trackForms': 1, // Tracks forms submissions.
28 | 'trackUsers': 1, // Tracks pageviews by users logged in to social networks.
29 | 'trackActions': 1, // Tracks 'mailto', 'tel', 'sms' and 'skype' actions.
30 | 'trackPrint': 1, // Tracks page print actions.
31 | 'trackOrientation': 1, // Tracks orientation change on mobile devices.
32 | 'trackAdblock': 0, // Tracks page views with blocked ads. (Experimental)
33 | 'trackErrorPages': 0, // Tracks error pages. (Experimental)
34 | 'sendHeartbeat': 0, // Sends heartbeat event. (Default interval 30 seconds)
35 | 'debugMode': 0, // Prints all requests to console.
36 | 'trackScroll': [25, 50, 75, 100], // Tracks scroll depth.
37 | 'trackMedia': ['html5', 'vimeo', 'youtube'], // Tracks HTML5 video, audio, Vimeo and YouTube players events.
38 | 'nonInteraction': ['adblock', 'audio', 'form', 'heartbeat',
39 | 'orientation', 'print', 'scroll', 'video'] // List of non interaction events.
40 | });
41 | ```
--------------------------------------------------------------------------------
/src/komito/trackers/social/users.js:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | /**
5 | * Defines komito.trackers.social.Users constructor.
6 | * @constructor
7 | */
8 | komito.trackers.social.Users = function() {
9 |
10 | /**
11 | * Mapping for tracking logged in users.
12 | * @type {!Object.}
13 | */
14 | var USERS = {
15 | 'Google': 'https://accounts.google.com/CheckCookie?continue=' +
16 | 'https%3A%2F%2Fwww.google.com%2Fintl%2Fen%2Fimages%2Flogos%2F' +
17 | 'accounts_logo.png&followup=https%3A%2F%2Fwww.google.com%2F' +
18 | 'intl%2Fen%2Fimages%2Flogos%2Faccounts_logo.png&' +
19 | 'chtml=LoginDoneHtml&checkedDomains=youtube&' +
20 | 'checkConnection=youtube%3A291%3A1'
21 | };
22 |
23 | /**
24 | * Tracks pageviews by users logged in to social networks.
25 | * @private
26 | */
27 | function init_() {
28 | /** @type {number} */ var sent = 0;
29 | /** @type {number} */ var attempts = 5;
30 | /** @type {string} */ var network;
31 |
32 | /**
33 | * @param {string} network The social network name.
34 | */
35 | function pageview(network) {
36 | komito.track(
37 | komito.SOCIAL_ACTION_TYPE, network, 'pageview', location.href);
38 | }
39 |
40 | /**
41 | * @param {!HTMLImageElement|!Image} image The image object.
42 | * @param {string} network The social network name.
43 | */
44 | function subscribe(image, network) {
45 | dom.events.addEventListener(image, dom.events.TYPE.LOAD, function() {
46 | pageview(network);
47 | });
48 | image.src = USERS[network];
49 | }
50 |
51 | /**
52 | * @param {function(function(?Object), boolean=)} fn getLoginStatus function.
53 | * @see https://developers.facebook.com/docs/reference/javascript/FB.getLoginStatus/
54 | */
55 | function getStatus(fn) {
56 | fn(function(response) {
57 | if (response && 'unknown' !== response['status'] && !sent++)
58 | pageview('Facebook');
59 | }, true);
60 | }
61 |
62 | function status() {
63 | /** @type {function(function(?Object), boolean=)} */
64 | var fn = dom.context['FB'] && dom.context['FB']['getLoginStatus'];
65 | if (fn) {
66 | getStatus(fn);
67 |
68 | dom.events.addEventListener(dom.context, 'message', function(e) {
69 | try {
70 | if ('facebook.com' === e['origin'].substr(-12) &&
71 | e['data'] &&
72 | ~e['data'].indexOf('xd_action=proxy_ready')) {
73 | getStatus(fn);
74 | }
75 | } catch (ex) {}
76 | });
77 |
78 | } else if (--attempts) {
79 | setTimeout(status, 2e3);
80 | }
81 | }
82 |
83 | for (network in USERS) {
84 | /** @type {!Image} */ var image = new Image(1, 1);
85 | subscribe(image, network);
86 | }
87 |
88 | komito.trackers.social.Facebook.init(status);
89 | }
90 |
91 | // Initializing Users events tracking.
92 | init_();
93 | };
94 |
--------------------------------------------------------------------------------
/src/glize/util/String.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview String utility methods.
3 | *
4 | * @see http://google.github.io/styleguide/javascriptguide.xml
5 | * @see http://developers.google.com/closure/compiler/docs/js-for-compiler
6 | */
7 |
8 |
9 | /**
10 | * String utility methods.
11 | * @namespace
12 | */
13 | util.String = {
14 | /**
15 | * Trims leading and trailing whitespace from the given string.
16 | * @param {string} str The string to trim.
17 | * @return {string} Returns the string stripped of whitespace.
18 | * @see {@link http://www.ecma-international.org/ecma-262/5.1/#sec-15.5.4.20}
19 | */
20 | trim: function(str) {
21 | return str.trim ? str.trim() : util.String.trimRight(
22 | util.String.trimLeft(str));
23 | },
24 |
25 | /**
26 | * Removes whitespace from the left end of the string.
27 | * @param {string} str The string to trim.
28 | * @return {string} Returns the string stripped of whitespace from left end.
29 | */
30 | trimLeft: function(str) {
31 | return str.trimLeft ? str.trimLeft() : str.replace(/^[\s\xa0]+/, '');
32 | },
33 |
34 | /**
35 | * Removes whitespace from the right end of the string.
36 | * @param {string} str The string to trim.
37 | * @return {string} Returns the string stripped of whitespace from right end.
38 | */
39 | trimRight: function(str) {
40 | return str.trimRight ? str.trimRight() : str.replace(/[\s\xa0]+$/, '');
41 | },
42 |
43 | /**
44 | * Checks whether str starts with prefix.
45 | * @param {string} str The string to be checked.
46 | * @param {string} prefix A string to look for at the start of
47 | * str.
48 | * @return {boolean} Returns true if string str
49 | * starts with the prefix.
50 | */
51 | startsWith: function(str, prefix) {
52 | return 0 === str.lastIndexOf(prefix, 0);
53 | },
54 |
55 | /**
56 | * Checks whether str ends with suffix.
57 | * @param {string} str The string to be checked.
58 | * @param {string} suffix A string to look for at the end of str.
59 | * @return {boolean} Returns true if string str
60 | * ends with the suffix.
61 | */
62 | endsWith: function(str, suffix) {
63 | /** @type {number} */ var index = str.lastIndexOf(suffix);
64 | return 0 <= index && index === str.length - suffix.length;
65 | },
66 |
67 | /**
68 | * Transforms the first character of each word to uppercase; other
69 | * characters are unaffected..
70 | * @param {string} str The string to be transformed.
71 | * @return {string} Returns transformed string.
72 | * @see http://www.w3.org/wiki/CSS/Properties/text-transform
73 | */
74 | capitalize: function(str) {
75 | /** @type {!Array.} */ var words = str.split(/\s+/);
76 | /** @type {number} */ var length = words.length;
77 | /** @type {number} */ var i = 0;
78 | /** @type {string} */ var word;
79 |
80 | for (; i < length; ++i) {
81 | word = words[i];
82 | words[i] = word.charAt(0).toUpperCase() + word.slice(1);
83 | }
84 |
85 | return words.join(' ');
86 | }
87 | };
88 |
--------------------------------------------------------------------------------
/src/komito/trackers/dom/__ns__.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Defines komito.trackers.dom namespace.
3 | * @namespace
4 | */
5 | komito.trackers.dom = {
6 | init: function() {
7 | komito.trackers.dom.Forms && new komito.trackers.dom.Forms;
8 | komito.trackers.dom.Links && new komito.trackers.dom.Links;
9 | komito.trackers.dom.Print && new komito.trackers.dom.Print;
10 | komito.trackers.dom.Scroll && new komito.trackers.dom.Scroll;
11 | komito.trackers.dom.Orientation && new komito.trackers.dom.Orientation;
12 | komito.trackers.dom.AdBlock && new komito.trackers.dom.AdBlock;
13 |
14 | komito.trackers.dom.trackHeartBeat_(+komito.config['sendHeartbeat']);
15 | komito.trackers.dom.trackErrorPages_();
16 | komito.trackers.dom.trackColorScheme_();
17 | komito.trackers.dom.trackRuntimeErrors_();
18 | },
19 |
20 | /**
21 | * Tracks heartbeat event.
22 | * @param {number} interval The heartbeat interval in seconds.
23 | * @see https://www.w3.org/TR/page-visibility/
24 | * @private
25 | */
26 | trackHeartBeat_: function(interval) {
27 | if (interval) {
28 | /** @type {number} */ var timer;
29 | interval = Math.max(interval, 30);
30 |
31 | dom.events.addEventListener(dom.document, 'visibilitychange', function() {
32 | if ('visible' === dom.document['visibilityState']) {
33 | timer = setInterval(function() {
34 | komito.track(komito.EVENT_ACTION_TYPE, 'heartbeat', interval + 's');
35 | }, 1E3 * interval);
36 | } else {
37 | timer && clearInterval(timer);
38 | }
39 | });
40 | }
41 | },
42 |
43 | /**
44 | * Tracks error pages.
45 | * @private
46 | */
47 | trackErrorPages_: function() {
48 | var page = komito.config['trackErrorPages'] && location.href;
49 |
50 | if (page) {
51 | komito.markAsNonInteractionEvent('errors');
52 | (new net.HttpRequest).doHead(/** @type {string} */(page), function(req) {
53 | if (399 < req.status) {
54 | komito.track(komito.EVENT_ACTION_TYPE, 'errors', req.status, page);
55 | }
56 | });
57 | }
58 | },
59 |
60 | /**
61 | * Tracks javascript runtime errors.
62 | * @see https://developer.mozilla.org/en-US/docs/Web/API/ErrorEvent
63 | * @private
64 | */
65 | trackRuntimeErrors_: function() {
66 | if (komito.config['trackErrors']) {
67 | komito.markAsNonInteractionEvent('errors');
68 | dom.events.addEventListener(dom.context, 'error', function(e) {
69 | komito.track(komito.EVENT_ACTION_TYPE, 'errors', e.message, e.filename);
70 | });
71 | }
72 | },
73 |
74 | /**
75 | * Tracks user preferred color scheme.
76 | * @see https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme
77 | * @private
78 | */
79 | trackColorScheme_: function() {
80 | if (komito.config['trackColorScheme'] && dom.context.matchMedia) {
81 | // https://github.com/Datamart/Komito/issues/38
82 | komito.markAsNonInteractionEvent('color-scheme');
83 |
84 | var query = '(prefers-color-scheme: dark)';
85 | var scheme = dom.context.matchMedia(query).matches ? 'dark' : 'light';
86 | komito.track(komito.EVENT_ACTION_TYPE, 'color-scheme', scheme);
87 | }
88 | }
89 | };
90 |
--------------------------------------------------------------------------------
/src/komito/trackers/media/html5.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview Komito Analytics tracker for HTML5 media elements.
3 | *
4 | * Adds DOM event listeners to