├── 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 [![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](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 | [![License](http://img.shields.io/:license-apache-blue.svg)](http://www.apache.org/licenses/LICENSE-2.0.html) [![Website](https://img.shields.io/website-up-down-green-red/https/komito.net.svg?style=flat)](https://komito.net) [![NPM version](https://img.shields.io/npm/v/komito-analytics.svg?style=flat)](https://npmjs.org/package/komito-analytics) [![NPM downloads](https://img.shields.io/npm/dm/komito-analytics.svg?style=flat)](https://npmjs.org/package/komito-analytics) [![Featured on Openbase](https://badges.openbase.com/js/featured/komito-analytics.svg?token=M+/9hZajk/0wvVbSAegFdsqngROcpFxgW93PereSBU0=)](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 | ![Analytics](https://www.google-analytics.com/collect?v=1&tid=UA-5065160-14&t=pageview&dh=github.com&dp=/Datamart/Komito&dt=GitHub%20README) 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 [![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](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 | [![License](http://img.shields.io/:license-apache-blue.svg)](http://www.apache.org/licenses/LICENSE-2.0.html) [![Website](https://img.shields.io/website-up-down-green-red/https/komito.net.svg?style=flat)](https://komito.net) [![NPM version](https://img.shields.io/npm/v/komito-analytics.svg?style=flat)](https://npmjs.org/package/komito-analytics) [![NPM downloads](https://img.shields.io/npm/dm/komito-analytics.svg?style=flat)](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