PageSourceSearch

https://dev.rutoken.ru/includes/js/analytics-support.js

js rutoken.ru collected 2026-09-24 09:00:12 UTC 7,699 bytes, 210 lines download raw bytes

1/**
2 * Provides utility functions for publishing analytics in Confluence, including support for adding
3 * and removing the 'src' parameter from URLs once an equivalent analytics event has been published.
4 *
5 * NOTE - there are plugins that access these utility functions on the global AJS.Confluence variable
6 * at the moment, once this is no longer true this code should be refactored to stop exposing functionality
7 * globally.
8 */
9define('confluence/analytics-support', [
10    'jquery',
11    'ajs',
12    'confluence/meta',
13    'window',
14    'document',
15    'confluence/api/url'
16], function(
17    $,
18    AJS,
19    Meta,
20    window,
21    document,
22    url
23) {
24    'use strict';
25
26    /**
27     * @exports confluence/analytics-support
28     */
29    var Analytics = Object.create(null);
30
31    var hasAnalyticsHandler;
32
33    /* eslint-disable vars-on-top, valid-jsdoc */
34    function srcAppendedUrl(urlString, analyticsSource) {
35        var parsedUrl = url.parseUrl(urlString);
36
37        if (parsedUrl.size !== 0) {
38            parsedUrl.get('queryParams').set('src', [analyticsSource]);
39        }
40        return url.formatUrl(parsedUrl);
41    }
42
43    function analyticsHandlerExists() {
44        if (typeof hasAnalyticsHandler === 'undefined') {
45            var eventsData = $._data(window, 'events');
46            hasAnalyticsHandler = eventsData && eventsData.analytics && eventsData.analytics.length > 0;
47        }
48        return hasAnalyticsHandler;
49    }
50
51    function replaceStateAfterCleaningUpAnalyticsParameters() {
52        var cleanUrl = Analytics.srcRemovedUrl(document.URL);
53        if (document.URL !== cleanUrl) {
54            window.history.replaceState(null, '', cleanUrl);
55        }
56    }
57
58    /**
59     * Returns true if the provided name (components[0].components[1].components[2]) is a valid analytics attribute.
60     *  This is true if the attribute name matches the pattern /src\.[^.]+\.[^.]+/
61     * @param {Array.<String>} components
62     */
63    function isAnalyticsAttribute(components) {
64        return components.length === 3 && components[0] === 'src';
65    }
66
67    /**
68     * Updates the href of the array of links to include a src parameter. The value of the src parameter is specified in analyticsSource
69     * @param {jQuery} links
70     * @param {string} analyticsSource
71     */
72    Analytics.setAnalyticsSource = function(links, analyticsSource) {
73        if (analyticsHandlerExists()) {
74            links.attr('href', function(i, href) {
75                return srcAppendedUrl(href, encodeURIComponent(analyticsSource));
76            });
77        }
78    };
79
80    Analytics.srcRemovedUrl = function(urlString) {
81        var parsedUrl = url.parseUrl(urlString);
82        parsedUrl.get('queryParams').delete('src');
83
84        var keys = Array.from(parsedUrl.get('queryParams').keys());
85        for (var i = 0; i < keys.length; i++) {
86            var property = keys[i];
87            var components = property.split('.');
88            if (isAnalyticsAttribute(components)) {
89                parsedUrl.get('queryParams').delete(property);
90            }
91            var isJwtTokenParam = property === 'jwt';
92            if (isJwtTokenParam) {
93                parsedUrl.get('queryParams').delete(property);
94            }
95        }
96        return url.formatUrl(parsedUrl);
97    };
98
99    /**
100     * Returns values of the src parameter from the specified URL as an array.
101     * @param urlString String that represents a URL to parse.
102     * @returns {src|*|string|Array} Array containing all 'src' parameter values.
103     */
104    Analytics.srcParamValues = function(urlString) {
105        var params = url.parseUrl(urlString).get('queryParams');
106        return params && params.get('src') ? params.get('src') : [];
107    };
108
109    /**
110     * Decodes the analytics attributes from the url
111     * Analytics attributes should be of the form <code>src.SOURCE.ATTRIBUTE_NAME=ATTRIBUTE_VALUE</code>
112     *
113     * We only take the first occurrence of a particular <code>src.SOURCE.ATTRIBUTE_NAME=ATTRIBUTE_VALUE</code> query param.
114     *
115     * @param {String} urlString String that represents a URL to parse.
116     * @returns {object} Map containing all 'src' attributes.
117     */
118    Analytics.srcAttrParamValues = function(urlString) {
119        var params = url.parseUrl(urlString).get('queryParams');
120
121        var sourceAttributes = Object.create(null);
122
123        var keys = Array.from(params.keys());
124        for (var i = 0; i < keys.length; i++) {
125            var property = keys[i];
126
127            var components = property.split('.');
128            if (isAnalyticsAttribute(components)) {
129                var source = components[1];
130                var attributeName = components[2];
131
132                // If this source object does not already exist we add it, and then put in the value
133                sourceAttributes[source] = sourceAttributes[source] || Object.create(null);
134                sourceAttributes[source][attributeName] = decodeURIComponent(params.get(property)[0]);
135            }
136        }
137
138        return sourceAttributes;
139    };
140
141    /**
142     * Extract analytics data from a url
143     * @param urlString String that represents the url to parse
144     * @returns {Array.<{src:String,attr:Object}>}
145     */
146    Analytics.extractAnalyticsData = function(urlString) {
147        var analyticsData = [];
148
149        var values = Analytics.srcParamValues(urlString);
150        var srcAttributes = Analytics.srcAttrParamValues(urlString);
151
152        for (var i = 0; i < values.length; i++) {
153            var source = values[i];
154            var attributes = srcAttributes[source] || {};
155            analyticsData.push({ src: source, attr: attributes });
156        }
157
158        return analyticsData;
159    };
160
161    /**
162     * Publishes a client-side analytics event. The event being published must adhere to our privacy policy, and also
163     * needs to be added to a plugin or product whitelist as per the instructions here:
164     * https://extranet.atlassian.com/display/AA/The+Developer%27s+Guide+to+Atlassian+Analytics
165     * @param name Name of the analytics event
166     * @param data Additional attributes
167     */
168    Analytics.publish = function(name, data) {
169        AJS.trigger('analytics', { name: name, data: data || {} });
170    };
171
172    Analytics.init = function() {
173        var analyticsData = Analytics.extractAnalyticsData(document.URL);
174
175        var defaultData = {
176            userKey: Meta.get('remote-user-key'),
177            pageID: Meta.get('page-id'),
178            draftID: Meta.get('draft-id')
179        };
180
181        if (analyticsData.length > 0) {
182            for (var i = 0; i < analyticsData.length; i++) {
183                // Events are published with a set of attributes, however individual
184                // attributes need to be added to the white list to be of any use
185                var analyticsEvent = analyticsData[i];
186                var data = $.extend({}, defaultData, analyticsEvent.attr);
187                Analytics.publish('confluence.viewpage.src.' + analyticsEvent.src, data);
188            }
189
190            // Check if browser supports HTML5 replaceState()
191            if (window.history && window.history.replaceState) {
192                replaceStateAfterCleaningUpAnalyticsParameters();
193            }
194        } else {
195            // CONFDEV-33536 - Confluence simplify journeys
196            // Adding generic event when src isn't available
197            Analytics.publish('confluence.viewpage.src.empty', defaultData);
198        }
199    };
200
201    return Analytics;
202});
203
204require('confluence/module-exporter').exportModuleAsGlobal('confluence/analytics-support', 'AJS.Confluence.Analytics', function(AnalyticsSupport) {
205    'use strict';
206
207    var AJS = require('ajs');
208
209    AJS.toInit(AnalyticsSupport.init);
210});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.