Greasy Fork

Shined up real nice.

gm4-polyfill-mach6

This utility is designed to ease authoring user scripts compatible with both Greasemonkey 4 and other/older user script engines.

This script should not be not be installed directly. It is a library for other scripts to include with the meta directive // @require https://greasyfork.org/scripts/372965-gm4-polyfill-mach6/code/gm4-polyfill-mach6.js?version=635207

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
/*
This helper script bridges compatibility between the Greasemonkey 4 APIs and
existing/legacy APIs.  Say for example your user script includes

    // @grant GM_getValue

And you'd like to be compatible with both Greasemonkey 3 and Greasemonkey 4
(and for that matter all versions of Violentmonkey, Tampermonkey, and any other
user script engine).  Add:

    // @grant GM.getValue
    // @require https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js

And switch to the new (GM-dot) APIs, which return promises.  If your script
is running in an engine that does not provide the new asynchronous APIs, this
helper will add them, based on the old APIs.

If you use `await` at the top level, you'll need to wrap your script in an
`async` function to be compatible with any user script engine besides
Greasemonkey 4.

    (async () => {
    let x = await GM.getValue('x');
    })();
*/

if (typeof GM == 'undefined') {
  this.GM = {};
}


if (typeof GM_addStyle == 'undefined') {
    this.GM_addStyle = function (aCss, aId) {
        'use strict';
        let head = document.getElementsByTagName('head')[0];
        if (!head) {
            head = document.documentElement;
        }
        let style = document.createElement('style');
        if (aId) {
            style.setAttribute('id', aId);
        }
        style.setAttribute('type', 'text/css');
        style.textContent = aCss;
        if (head) {
            return head.appendChild(style);
        }
        else {
            return null;
        };
    }
}


if (typeof GM_registerMenuCommand == 'undefined') {
  this.GM_registerMenuCommand = function(caption, commandFunc, accessKey){
    if (!document.body) {
      if (document.readyState === 'loading'
        && document.documentElement && document.documentElement.localName === 'html') {
        new MutationObserver(function(mutations, observer){
          if (document.body) {
            observer.disconnect();
            GM_registerMenuCommand(caption, commandFunc, accessKey);
          }
        }).observe(document.documentElement, {childList: true});
      } else {
        console.error('GM_registerMenuCommand got no body.');
      }
      return;
    }
    let contextMenu = document.body.getAttribute('contextmenu');
    let menu = (contextMenu ? document.querySelector('menu#' + contextMenu) : null);
    if (!menu) {
      menu = document.createElement('menu');
      menu.setAttribute('id', 'gm-registered-menu');
      menu.setAttribute('type', 'context');
      document.body.appendChild(menu);
      document.body.setAttribute('contextmenu', 'gm-registered-menu');
    }
    let menuItem = document.createElement('menuitem');
    menuItem.textContent = caption;
    menuItem.addEventListener('click', commandFunc, true);
    menu.appendChild(menuItem);
  };
}


if (typeof GM_getResourceText == 'undefined') {
  this.GM_getResourceText = function(aRes){
    'use strict';
    return GM.getResourceUrl(aRes)
      .then( function(url){return fetch(url)})
      .then( function(resp){return resp.text()})
      .catch(function(error) {
        GM.log('Request failed', error);
        return null;
      });
  };
}


Object.entries({
  'log': console.log.bind(console),  // Pale Moon compatibility.  See #13.
  'info': GM_info,
}).forEach(function([newKey, old]){
  if (old && (typeof GM[newKey] == 'undefined')) {
    GM[newKey] = old;
  }
});


Object.entries({
    'GM_addStyle': 'addStyle',
    'GM_deleteValue': 'deleteValue',
    'GM_getResourceURL': 'getResourceUrl',
    'GM_getValue': 'getValue',
    'GM_listValues': 'listValues',
    'GM_notification': 'notification',
    'GM_openInTab': 'openInTab',
    'GM_registerMenuCommand': 'registerMenuCommand',
    'GM_setClipboard': 'setClipboard',
    'GM_setValue': 'setValue',
    'GM_xmlhttpRequest': 'xmlHttpRequest',
    'GM_getResourceText': 'getResourceText',
}).forEach( function([oldKey, newKey]){
    let old = this[oldKey];
    if (old && (typeof GM[newKey] == 'undefined')) {
        if (oldKey === 'GM_addStyle') {
            GM[newKey] = function(...args) {
                return old(args);
            };
        }
        else {
            GM[newKey] = function(...args) {
                return new Promise( function(resolve, reject){
                    try {
                        resolve(old.apply(this, args));
                    } catch (e) {
                        reject(e);
                    }
                }.bind(this));
            };
        }
    }
}.bind(this));