ESRMeter taken from http://members.shaw.ca/swstuff/esrmeter.html (find website on internet archive). Offered here with permission.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

270 lines
7.2 KiB

5 years ago
  1. /* eslint-disable no-var, semi, prefer-arrow-callback, prefer-template */
  2. /**
  3. * Collection of methods for sending analytics events to Archive.org's analytics server.
  4. *
  5. * These events are used for internal stats and sent (in anonymized form) to Google Analytics.
  6. *
  7. * @see analytics.md
  8. *
  9. * @type {Object}
  10. */
  11. window.archive_analytics = (function defineArchiveAnalytics() {
  12. var DEFAULT_SERVICE = 'ao_2';
  13. var startTime = new Date();
  14. /**
  15. * @return {Boolean}
  16. */
  17. function isPerformanceTimingApiSupported() {
  18. return 'performance' in window && 'timing' in window.performance;
  19. }
  20. /**
  21. * Determines how many milliseconds elapsed between the browser starting to parse the DOM and
  22. * the current time.
  23. *
  24. * Uses the Performance API or a fallback value if it's not available.
  25. *
  26. * @see https://developer.mozilla.org/en-US/docs/Web/API/Performance_API
  27. *
  28. * @return {Number}
  29. */
  30. function getLoadTime() {
  31. var start;
  32. if (isPerformanceTimingApiSupported())
  33. start = window.performance.timing.domLoading;
  34. else
  35. start = startTime.getTime();
  36. return new Date().getTime() - start;
  37. }
  38. /**
  39. * Determines how many milliseconds elapsed between the user navigating to the page and
  40. * the current time.
  41. *
  42. * @see https://developer.mozilla.org/en-US/docs/Web/API/Performance_API
  43. *
  44. * @return {Number|null} null if the browser doesn't support the Performance API
  45. */
  46. function getNavToDoneTime() {
  47. if (!isPerformanceTimingApiSupported())
  48. return null;
  49. return new Date().getTime() - window.performance.timing.navigationStart;
  50. }
  51. /**
  52. * Performs an arithmetic calculation on a string with a number and unit, while maintaining
  53. * the unit.
  54. *
  55. * @param {String} original value to modify, with a unit
  56. * @param {Function} doOperation accepts one Number parameter, returns a Number
  57. * @returns {String}
  58. */
  59. function computeWithUnit(original, doOperation) {
  60. var number = parseFloat(original, 10);
  61. var unit = original.replace(/(\d*\.\d+)|\d+/, '');
  62. return doOperation(number) + unit;
  63. }
  64. /**
  65. * Computes the default font size of the browser.
  66. *
  67. * @returns {String|null} computed font-size with units (typically pixels), null if it cannot be computed
  68. */
  69. function getDefaultFontSize() {
  70. var fontSizeStr;
  71. if (!('getComputedStyle' in window))
  72. return null;
  73. fontSizeStr = window.getComputedStyle(document.documentElement).fontSize;
  74. // Don't modify the value if tracking book reader.
  75. if (document.documentElement.classList.contains('BookReaderRoot'))
  76. return fontSizeStr;
  77. return computeWithUnit(fontSizeStr, function reverseBootstrapFontSize(number) {
  78. // Undo the 62.5% size applied in the Bootstrap CSS.
  79. return number * 1.6;
  80. });
  81. }
  82. return {
  83. /**
  84. * @type {String|null}
  85. */
  86. service: null,
  87. /**
  88. * Key-value pairs to send in pageviews (you can read this after a pageview to see what was
  89. * sent).
  90. *
  91. * @type {Object}
  92. */
  93. values: {},
  94. /**
  95. * @param {Object} values
  96. * @param {Function} [onload_callback] (deprecated) callback to invoke once ping to analytics server is done
  97. * @param {Boolean} [augment_for_ao_site] (deprecated) if true, add some archive.org site-specific values
  98. */
  99. send_ping: function send_ping(values, onload_callback, augment_for_ao_site) {
  100. var img_src = "//analytics.archive.org/0.gif";
  101. if (!values)
  102. values = {};
  103. function format_ping(values) {
  104. var ret = [];
  105. var count = 2;
  106. var version = 2;
  107. for (var data in values) {
  108. ret.push(encodeURIComponent(data) + "=" + encodeURIComponent(values[data]));
  109. count = count + 1;
  110. }
  111. ret.push('version=' + version);
  112. ret.push('count=' + count);
  113. return ret.join("&");
  114. }
  115. // Automatically set service.
  116. if (!values.service && this.service)
  117. values.service = this.service;
  118. if (augment_for_ao_site && !values.service)
  119. values.service = DEFAULT_SERVICE;
  120. var string = format_ping(values);
  121. var loadtime_img = new Image(100,25);
  122. if (onload_callback && typeof(onload_callback)=='function')
  123. loadtime_img.onload = onload_callback;
  124. loadtime_img.src = img_src + "?" + string;
  125. },
  126. send_scroll_fetch_event: function send_scroll_fetch_event(page) {
  127. var values = {
  128. kind: 'event',
  129. ec: 'page_action',
  130. ea: 'scroll_fetch',
  131. el: location.pathname,
  132. ev: page, // int
  133. cache_bust: Math.random()
  134. };
  135. var loadTime = getLoadTime();
  136. var navToDoneTime = getNavToDoneTime();
  137. if (loadTime)
  138. values.loadtime = loadTime;
  139. if (navToDoneTime)
  140. values.nav_to_done_ms = navToDoneTime;
  141. this.send_ping(values);
  142. },
  143. send_scroll_fetch_base_event: function send_scroll_fetch_base_event() {
  144. var values = {
  145. kind: 'event',
  146. ec: 'page_action',
  147. ea: 'scroll_fetch_base',
  148. el: location.pathname,
  149. cache_bust: Math.random(),
  150. };
  151. var loadTime = getLoadTime();
  152. var navToDoneTime = getNavToDoneTime();
  153. if (loadTime)
  154. values.loadtime = loadTime;
  155. if (navToDoneTime)
  156. values.nav_to_done_ms = navToDoneTime;
  157. this.send_ping(values);
  158. },
  159. /**
  160. * @param {Object} options
  161. * @param {String} [options.mediaType]
  162. */
  163. send_pageview: function send_pageview(options) {
  164. var settings = options || {};
  165. var defaultFontSize;
  166. var loadTime = getLoadTime();
  167. var mediaType = settings.mediaType;
  168. var navToDoneTime = getNavToDoneTime();
  169. /**
  170. * @return {String}
  171. */
  172. function get_locale() {
  173. if (navigator) {
  174. if (navigator.language)
  175. return navigator.language;
  176. else if (navigator.browserLanguage)
  177. return navigator.browserLanguage;
  178. else if (navigator.systemLanguage)
  179. return navigator.systemLanguage;
  180. else if (navigator.userLanguage)
  181. return navigator.userLanguage;
  182. }
  183. return '';
  184. }
  185. defaultFontSize = getDefaultFontSize();
  186. // Set field values
  187. this.values.kind = 'pageview';
  188. this.values.timediff = (new Date().getTimezoneOffset()/60)*(-1); // *timezone* diff from UTC
  189. this.values.locale = get_locale();
  190. this.values.referrer = (document.referrer == '' ? '-' : document.referrer);
  191. if (loadTime)
  192. this.values.loadtime = loadTime;
  193. if (navToDoneTime)
  194. this.values.nav_to_done_ms = navToDoneTime;
  195. if (defaultFontSize)
  196. this.values.ga_cd1 = defaultFontSize;
  197. if ('devicePixelRatio' in window)
  198. this.values.ga_cd2 = window.devicePixelRatio;
  199. if (mediaType)
  200. this.values.ga_cd3 = mediaType;
  201. this.send_ping(this.values);
  202. },
  203. /**
  204. * @param {Object} options see this.send_pageview options
  205. */
  206. send_pageview_on_load: function send_pageview_on_load(options) {
  207. var self = this;
  208. window.addEventListener('load', function send_pageview_with_options() {
  209. self.send_pageview(options);
  210. });
  211. },
  212. /**
  213. * @returns {Object[]}
  214. */
  215. get_data_packets: function get_data_packets() {
  216. return [this.values];
  217. },
  218. };
  219. }());