Swatch Internet Time

Convert times to Swatch Internet Time .beats

目前为 2020-04-12 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Swatch Internet Time
  3. // @author Nik Rolls
  4. // @description Convert times to Swatch Internet Time .beats
  5. // @match *://*/*
  6. // @require https://greasyfork.org/scripts/395037-monkeyconfig-modern/code/MonkeyConfig%20Modern.js?version=764968
  7. // @grant GM_getMetadata
  8. // @grant GM_getValue
  9. // @grant GM_setValue
  10. // @grant GM_registerMenuCommand
  11. // @grant GM_addStyle
  12. // @version 0.0.1.20200412020302
  13. // @namespace https://greasyfork.org/users/503103
  14. // ==/UserScript==
  15.  
  16. (function() {
  17. 'use strict';
  18.  
  19. const cfg = new MonkeyConfig({
  20. menuCommand: true,
  21. params: {
  22. global_timezone: {
  23. label: 'Use global BMT timezone',
  24. type: 'checkbox',
  25. default: true
  26. }
  27. }
  28. });
  29.  
  30. let interval = null;
  31.  
  32. document.addEventListener('visibilitychange', detectVisibility);
  33. detectVisibility();
  34.  
  35. function detectVisibility() {
  36. if (document.visibilityState === 'visible') {
  37. startWatching();
  38. } else {
  39. stopWatching();
  40. }
  41. }
  42.  
  43. function startWatching() {
  44. augmentAllTimes();
  45. interval = window.setInterval(augmentAllTimes, 1000);
  46. }
  47.  
  48. function stopWatching() {
  49. if (interval) {
  50. window.clearInterval(interval);
  51. }
  52. }
  53.  
  54. function augmentAllTimes() {
  55. do {} while (augmentTimes())
  56. }
  57.  
  58. function augmentTimes() {
  59. const items = document.evaluate('/html/body//*[not(self::style or self::script or self::time or self::input or self::textarea or boolean(@contenteditable)) and text()[contains(.,":0") or contains(.,":1") or contains(.,":2") or contains(.,":3") or contains(.,":4") or contains(.,":5") or contains(.,":6") or contains(.,":7") or contains(.,":8") or contains(.,":9") or contains(.,"0pm") or contains(.,"1pm") or contains(.,"2pm") or contains(.,"3pm") or contains(.,"4pm") or contains(.,"5pm") or contains(.,"6pm") or contains(.,"7pm") or contains(.,"8pm") or contains(.,"9pm") or contains(.,"0 pm") or contains(.,"1 pm") or contains(.,"2 pm") or contains(.,"3 pm") or contains(.,"4 pm") or contains(.,"5 pm") or contains(.,"6 pm") or contains(.,"7 pm") or contains(.,"8 pm") or contains(.,"9 pm") or contains(.,"0am") or contains(.,"1am") or contains(.,"2am") or contains(.,"3am") or contains(.,"4am") or contains(.,"5am") or contains(.,"6am") or contains(.,"7am") or contains(.,"8am") or contains(.,"9am") or contains(.,"0 am") or contains(.,"1 am") or contains(.,"2 am") or contains(.,"3 am") or contains(.,"4 am") or contains(.,"5 am") or contains(.,"6 am") or contains(.,"7 am") or contains(.,"8 am") or contains(.,"9 am")]]', document.body, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  60. for (var i = 0; i < items.snapshotLength; i++) {
  61. const item = items.snapshotItem(i);
  62. if (item.isContentEditable) {
  63. return;
  64. }
  65.  
  66. const pattern = /(?:(?:([1-9][0-9]?(?::\d{2})*)\s*(a\.?m\.?|p\.?m\.?))|(\d{1,2}?:\d{2}(?::\d{2}(?:\.\d+)?)?))/i;
  67. const match = item.innerText.match(pattern);
  68. if (match) {
  69. let dateTime;
  70.  
  71. if (match[1]) {
  72. const time = match[1];
  73. const meridiem = match[2].replace(/\./g, '');
  74. dateTime = createDateTime(time, meridiem);
  75. } else if (match[3]) {
  76. dateTime = createDateTime(match[3]);
  77. }
  78.  
  79. if (dateTime) {
  80. const internetTime = convertToInternetTime(dateTime);
  81. const textNodes = Array.from(item.childNodes).filter(node => node.nodeType === Node.TEXT_NODE);
  82. textNodes.forEach((textNode) => {
  83. const textParts = textNode.textContent.match(new RegExp(`(.*)(${match[0]})(.*)`));
  84. if (textParts) {
  85. const newParts = [];
  86.  
  87. if (textParts[1]) {
  88. newParts.push(document.createTextNode(textParts[1]));
  89. }
  90.  
  91. if (textParts[2]) {
  92. const timeNode = document.createElement('time');
  93. timeNode.dateTime = dateTime.toISOString().replace(/^.*T|\+.*$|Z.*$/g, '');
  94. timeNode.title = match[0];
  95. timeNode.textContent = '@' + internetTime;
  96. newParts.push(timeNode);
  97. }
  98.  
  99. if (textParts[3]) {
  100. newParts.push(document.createTextNode(textParts[3]));
  101. }
  102. newParts.forEach(part => item.insertBefore(part, textNode));
  103. textNode.remove();
  104. }
  105. });
  106. }
  107. }
  108. }
  109.  
  110. return items.snapshotLength;
  111. }
  112.  
  113. function createDateTime(time, meridiem) {
  114. const timeParts = time.match(/\d+/g);
  115. const dateTime = new Date();
  116. dateTime.setHours(meridiem && timeParts[0] == 12 ? 0 : timeParts[0], timeParts[1] || 0, timeParts[2] || 0, timeParts[3] || 0);
  117. if (meridiem && meridiem.startsWith('p')) {
  118. dateTime.setHours(dateTime.getHours() + 12);
  119. }
  120. return dateTime;
  121. }
  122.  
  123. function convertToInternetTime(dateTime) {
  124. dateTime = new Date(dateTime.getTime());
  125.  
  126. if (cfg.get('global_timezone')) {
  127. dateTime.setMinutes(dateTime.getMinutes() + dateTime.getTimezoneOffset() + 60);
  128. }
  129.  
  130. const midnight = new Date(dateTime.getTime());
  131. midnight.setHours(0, 0, 0, 0);
  132.  
  133. const msPastMidnight = dateTime - midnight;
  134. const decimal = (msPastMidnight / 86400000) * 1000;
  135. return Math.floor((decimal + Number.EPSILON) * 100) / 100
  136. }
  137. })();