├── README.md ├── LICENSE └── gm4-polyfill.js /README.md: -------------------------------------------------------------------------------- 1 | This utility is designed to ease authoring user scripts compatible with 2 | both Greasemonkey 4 and other/older user script engines. 3 | 4 | Include it in your script like: 5 | 6 | // @require https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js 7 | 8 | Read the source of the file for more details. 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Anthony Lieuallen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /gm4-polyfill.js: -------------------------------------------------------------------------------- 1 | /* 2 | This helper script bridges compatibility between the Greasemonkey 4 APIs and 3 | existing/legacy APIs. Say for example your user script includes 4 | 5 | // @grant GM_getValue 6 | 7 | And you'd like to be compatible with both Greasemonkey 3 and Greasemonkey 4 8 | (and for that matter all versions of Violentmonkey, Tampermonkey, and any other 9 | user script engine). Add: 10 | 11 | // @grant GM.getValue 12 | // @require https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js 13 | 14 | And switch to the new (GM-dot) APIs, which return promises. If your script 15 | is running in an engine that does not provide the new asynchronous APIs, this 16 | helper will add them, based on the old APIs. 17 | 18 | If you use `await` at the top level, you'll need to wrap your script in an 19 | `async` function to be compatible with any user script engine besides 20 | Greasemonkey 4. 21 | 22 | (async () => { 23 | let x = await GM.getValue('x'); 24 | })(); 25 | */ 26 | 27 | if (typeof GM == 'undefined') { 28 | this.GM = {}; 29 | } 30 | 31 | 32 | if (typeof GM_addStyle == 'undefined') { 33 | this.GM_addStyle = (aCss) => { 34 | 'use strict'; 35 | let head = document.getElementsByTagName('head')[0]; 36 | if (head) { 37 | let style = document.createElement('style'); 38 | style.setAttribute('type', 'text/css'); 39 | style.textContent = aCss; 40 | head.appendChild(style); 41 | return style; 42 | } 43 | return null; 44 | }; 45 | } 46 | 47 | 48 | if (typeof GM_registerMenuCommand == 'undefined') { 49 | this.GM_registerMenuCommand = (caption, commandFunc, accessKey) => { 50 | if (!document.body) { 51 | if (document.readyState === 'loading' 52 | && document.documentElement && document.documentElement.localName === 'html') { 53 | new MutationObserver((mutations, observer) => { 54 | if (document.body) { 55 | observer.disconnect(); 56 | GM_registerMenuCommand(caption, commandFunc, accessKey); 57 | } 58 | }).observe(document.documentElement, {childList: true}); 59 | } else { 60 | console.error('GM_registerMenuCommand got no body.'); 61 | } 62 | return; 63 | } 64 | let contextMenu = document.body.getAttribute('contextmenu'); 65 | let menu = (contextMenu ? document.querySelector('menu#' + contextMenu) : null); 66 | if (!menu) { 67 | menu = document.createElement('menu'); 68 | menu.setAttribute('id', 'gm-registered-menu'); 69 | menu.setAttribute('type', 'context'); 70 | document.body.appendChild(menu); 71 | document.body.setAttribute('contextmenu', 'gm-registered-menu'); 72 | } 73 | let menuItem = document.createElement('menuitem'); 74 | menuItem.textContent = caption; 75 | menuItem.addEventListener('click', commandFunc, true); 76 | menu.appendChild(menuItem); 77 | }; 78 | } 79 | 80 | 81 | if (typeof GM_getResourceText == 'undefined') { 82 | this.GM_getResourceText = (aRes) => { 83 | 'use strict'; 84 | return GM.getResourceUrl(aRes) 85 | .then(url => fetch(url)) 86 | .then(resp => resp.text()) 87 | .catch(function(error) { 88 | GM.log('Request failed', error); 89 | return null; 90 | }); 91 | }; 92 | } 93 | 94 | 95 | Object.entries({ 96 | 'log': console.log.bind(console), // Pale Moon compatibility. See #13. 97 | 'info': GM_info, 98 | }).forEach(([newKey, old]) => { 99 | if (old && (typeof GM[newKey] == 'undefined')) { 100 | GM[newKey] = old; 101 | } 102 | }); 103 | 104 | 105 | Object.entries({ 106 | 'GM_addStyle': 'addStyle', 107 | 'GM_deleteValue': 'deleteValue', 108 | 'GM_getResourceURL': 'getResourceUrl', 109 | 'GM_getValue': 'getValue', 110 | 'GM_listValues': 'listValues', 111 | 'GM_notification': 'notification', 112 | 'GM_openInTab': 'openInTab', 113 | 'GM_registerMenuCommand': 'registerMenuCommand', 114 | 'GM_setClipboard': 'setClipboard', 115 | 'GM_setValue': 'setValue', 116 | 'GM_xmlhttpRequest': 'xmlHttpRequest', 117 | 'GM_getResourceText': 'getResourceText', 118 | }).forEach(([oldKey, newKey]) => { 119 | let old = this[oldKey]; 120 | if (old && (typeof GM[newKey] == 'undefined')) { 121 | GM[newKey] = function(...args) { 122 | return new Promise((resolve, reject) => { 123 | try { 124 | resolve(old.apply(this, args)); 125 | } catch (e) { 126 | reject(e); 127 | } 128 | }); 129 | }; 130 | } 131 | }); 132 | --------------------------------------------------------------------------------