Detect-Nav

Detect url change on Chrome/Firefox (Greasemonkey, Firemonkey, Violentmonkey, Tampermonkey)

目前为 2024-08-16 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name Detect-Nav
  3. // @description Detect url change on Chrome/Firefox (Greasemonkey, Firemonkey, Violentmonkey, Tampermonkey)
  4. // @author GTK
  5. // @namespace https://greasyfork.org/en/users/1352961-gtk
  6. // @version 0.1
  7. // @match *://*/*
  8. // @grant none
  9. // @run-at document-start
  10. // @noframes
  11. // ==/UserScript==
  12.  
  13.  
  14. // ============================================================
  15. // `GMCompat` Compatibility shim
  16. // adapted from `https://github.com/chocolateboy/gm-compat`
  17. // ============================================================
  18.  
  19. const $unsafeWindow = (typeof unsafeWindow !== 'undefined') ? unsafeWindow.wrappedJSObject || unsafeWindow : window;
  20. const GMCompat = Object.freeze({
  21. unsafeWindow: $unsafeWindow,
  22.  
  23. CLONE_INTO_OPTIONS: {
  24. cloneFunctions: true,
  25. target: $unsafeWindow,
  26. wrapReflectors: true
  27. },
  28.  
  29. EXPORT_FUNCTION_OPTIONS: {
  30. target: $unsafeWindow,
  31. },
  32.  
  33. apply: function($this, fn, _args) {
  34. const args = [].slice.call(_args)
  35. return fn.apply($this, this.cloneInto(args))
  36. },
  37.  
  38. call: function($this, fn, ..._args) {
  39. const args = this.cloneInto(_args)
  40. return fn.call($this, ...args)
  41. },
  42.  
  43. cloneInto: function(object, _options) {
  44. const options = Object.assign({}, this.CLONE_INTO_OPTIONS, _options)
  45. const _cloneInto = (typeof cloneInto === 'function') ? cloneInto : object => object
  46. return _cloneInto(object, options.target, options)
  47. },
  48.  
  49. export: function(value, options) {
  50. return (typeof value === 'function') ? this.exportFunction(value, options) : this.cloneInto(value, options)
  51. },
  52.  
  53. exportFunction: function(fn, _options) {
  54. const options = Object.assign({}, this.EXPORT_FUNCTION_OPTIONS, _options)
  55. const _exportFunction = (typeof exportFunction === 'function') ? exportFunction : (fn, { defineAs, target = $unsafeWindow } = {}) => { return defineAs ? (target[defineAs] = fn) : fn }
  56.  
  57. return _exportFunction(fn, options.target, options)
  58. },
  59.  
  60. unwrap: function(value) {
  61. return value ? (value.wrappedJSObject || value) : value
  62. }
  63. });
  64.  
  65.  
  66. // ============================================================
  67. // Navigation detection & CustomEvent dispatch
  68. // ============================================================
  69.  
  70. if (window.navigation) {
  71. console.info('Using Navigation API.');
  72. window.navigation.addEventListener('navigatesuccess', e => {
  73. notify(e.type, e.currentTarget.currentEntry.url);
  74. });
  75.  
  76. } else if (window.onurlchange === null) {
  77. console.info('Using window.onurlchange.');
  78. window.addEventListener('urlchange', e => {
  79. notify('urlchange', e.url);
  80. });
  81.  
  82. } else {
  83. console.info('Using patch.');
  84. (window.location.hostname === 'www.youtube.com' ? handleYoutube : patchHistory)();
  85. }
  86.  
  87.  
  88.  
  89. let oldUrl;
  90. function notify(method, url){
  91. const absUrl = new URL(url || window.location.href, window.location.origin).href;
  92. const detail = GMCompat.export({ method: method, oldUrl: oldUrl, newUrl: absUrl });
  93. const event = new CustomEvent('detectnavigate', { bubbles: true, detail: detail });
  94. document.dispatchEvent(event);
  95. oldUrl = absUrl;
  96. }
  97.  
  98. function patchHistory(){
  99. ['pushState', 'replaceState'].forEach(method => {
  100. const original = GMCompat.unsafeWindow.history[method];
  101. const patched = function(){
  102. GMCompat.apply(this, original, arguments);
  103. notify(method, arguments[2]);
  104. };
  105.  
  106. GMCompat.unsafeWindow.history[method] = GMCompat.export(patched);
  107. });
  108.  
  109. window.addEventListener('popstate', e => {
  110. notify(e.type);
  111. });
  112. }
  113.  
  114. function handleYoutube(){
  115. window.addEventListener('yt-navigate-finish', e => {
  116. notify(e.type, e.detail.response.url);
  117. });
  118. }