A metadata block is a section of a User script that does not execute any code, but describes the script. The main metadata block is called the UserScript metadata block and typically contains the script name, namespace, description, and Script injection rules.
Any metadata blocks appear in JavaScript comments and may appear anywhere in the top level Greasemonkey code scope of the script, but is usually near the top of the file. It begins with the line:
// ==UserScript==
... and ends with:
// ==/UserScript==
Additional configuration is always between those lines is in the format of:
// @key value
If this metadata block includes a key that Greasemonkey does not understand or tab characters, it will simply be ignored. Code may also occur in-between the header and footer comments. The header comment must always begin on a newline and have no whitespace preceding except for a newline in the text stream. Nested UserScript metadata blocks are not supported. Key names are typically constructed from alphanumeric characters and are case sensitive.
User script hosting sites may utilize a XML styled name prefix to identify the origin of the key. An example of this is prefix:key.
Some sites also support their own evolved metadata block, such as OpenUserJS.org, to minimize collisions with other sites and UserScript engines that define their own standards:
// ==OpenUserJS==
... and ends with:
// ==/OpenUserJS==
Additional site specific configuration is always between those lines is in the format of:
// @key value
Some sites also offer a cross-site compatible metadata block for including library scripts with:
// ==UserLibrary==
... and ends with:
// ==/UserLibrary==
Additional library specific configuration is always between those lines is in the format of:
// @key value
⬆ ⬇ | Other Keys | Examples | Caveats | See Also | Notes
// ==UserScript==
// @key value
// ==/UserScript==
// ==OpenUserJS==
// @key value
// ==/OpenUserJS==
Value: Object
Compatibility: Greasemonkey 0.2.5+
Keys
Properties | ||||
---|---|---|---|---|
@name | @run-at | @require | @version | |
@namespace | @include | @resource | @updateURL | |
@description | @exclude | @grant | @installURL | |
@icon | @match | @unwrap | @downloadURL | |
@author | @noframes | |||
@homepageURL |
Blocks: UserScript / UserLibrary
Value: String
Compatibility: Greasemonkey 0.2.5+
Usage:
// @name My Script
Compatibility: Greasemonkey 2.2.0+
Usage:
// @name My Script
// @name:cs Můj skript
// @name:es-MX Mi guión
// @name:ru Мой сценарий
Blocks: UserScript
Value: String
Usage:
// @namespace https://www.example.com/gmscripts
While the namespace is non-semantic, it should be your prefered internet homepage URI according to the W3C standards specification. If no namespace is provided, it is assumed to be the domain from which the script is installed. Since a script can live on various servers or on a local file system, authors may choose to omit this key when publishing on userscripts.org⁸⁰⁸⁰⋔ and let Greasemonkey supply one automatically. If you are creating one locally, authors may choose the URI specification standard of http://localhost
for the value as being an anonymous local script.
Common String Values
Script Host Chosen Namespace via Greasemonkey
Usage: Omitted @namespace in source userscript file
https://userscripts.org/scripts/source/scriptid.user.js
maps to// @namespace userscripts.org
https://example.org/scripts/filename.user.js
maps to// @namespace example.org
http://localhost/scripts/filename.user.js
maps to// @namespace localhost
tag URI
Usage: "tag:" taggingEntity ":" specific [ "#" fragment ]
// @namespace tag:johndoe@example.com,2009:johndoe
// @namespace tag:johndoe@example.net,2010-05:johndoe/pathto
// @namespace tag:userscripts.org,2005-06-19T23:33:49Z:JohnDoe:DescriptionWithNoSpaces:etc:etc:etc
id [[ " + " id ], [ " + " id ], ...]
// @namespace anotherjesse
// @namespace userid + vanityid + Firstname Lastname
scheme "://" domain [ "." tld ][ "/" pathto ]
// @namespace http://localhost
// @namespace http://localhost.localdomain
// @namespace https://userscripts.org/users/userid
// @namespace https://example.org/pathto/script
The NoScript Security Suite add-on currently utilizes namespace filtering, and may sanitize user scripts that are not listed in the XSS white-list section of the options dialog.
Blocks: UserScript / UserLibrary
Value: String
Usage:
// @description This script even does the laundry!
Blocks: UserScript
Value: URL
Compatibility: Greasemonkey 0.9.0+
Usage:
// @icon https://example.com/icon.png
Blocks: UserScript / OpenUserJS
Value: String
Compatibility: Greasemonkey 3.5.0+
Usage:
■ UserScript
// @author Author <Author email> (Author Website)
■ OpenUserJS
// @author sizzle
Blocks: UserScript
Value: URI
Compatibility: Greasemonkey 3.5.0+
Usage:
// @homepageURL https://example.com
Blocks: UserScript
Value: String
Compatibility: Greasemonkey 0.9.8+
Usage:
// @run-at document-end
Default: document-end
document-end
and is suitable for most situations. It is not required to insert this into the metadata block as this key/value pair is the default. However some scripts may benefit from early injection at the document-start
.Blocks: UserScript
Value: String
Usage:
// @include https://www.example.com/*
Blocks: UserScript
Value: String
Usage:
// @exclude https://www.example.com/foo/*
Blocks: UserScript
Value: String
Compatibility: Greasemonkey 0.9.8+
Usage:
// @match https://www.example.com/foo/*
Blocks: UserScript
Value: undefined
Compatibility: Greasemonkey 2.3.0+
Usage:
// @noframes
Blocks: UserScript
Value: String
Compatibility: Greasemonkey 0.8.0+
Usage:
// @require foo.js
Blocks: UserScript
Value: String
Compatibility: Greasemonkey 0.8.0+
Usage:
// @resource resourceName https://www.example.com/resource.png
Blocks: UserScript
Value: String
Compatibility: Greasemonkey 1.0+
Usage:
// @grant GM_addStyle
// @grant GM_deleteValue
// @grant GM_getResourceText
// @grant GM_getResourceURL
// @grant GM_getValue
// @grant GM_listValues
// @grant GM_log
// @grant GM_openInTab
// @grant GM_registerMenuCommand
// @grant GM_setClipboard
// @grant GM_setValue
// @grant GM_xmlhttpRequest
// @grant unsafeWindow
or
// @grant none
// @grant none
will be the default. If using // @grant none
the code will be assumed to run in the restricted (content scope) namespace.// @grant none
may be prone to detection by their respective target sites that they are injected into. There are additional considerations if using a framework like jQuery or YUI in a user script and the target site also uses one of them.Blocks: UserScript
Value: undefined
Compatibility: Greasemonkey 0.8.1 - 0.9.22
Usage:
// @unwrap
(function(){ /* script source */ })();
var sidebar = "foo";
. This will return a script error of Illegal value, and script execution will cease. See also greasemonkey.dejavu.com#108.Blocks: UserScript / UserLibrary
Value: String or Number
Compatibility: Greasemonkey 0.9.0+
Usage:
// @version 0.0.1
Blocks: UserScript
Value: URL
Compatibility: Greasemonkey 0.9.12+
Usage:
// @updateURL https://www.example.com/script.user.js
or
// @updateURL https://userscripts.org/scripts/source/scriptid.meta.js
text/x-userscript-meta
, should automatically be sent internally to reduce bandwidth and a quicker response by returning only the Metadata Block components supported from the host. Minimal requirement for a successful response is a @version line inside the UserScript metadata block.If this key is set when publishing to userscripts.org⁸⁰⁸⁰⋔, it is highly recommended to use the meta.js routine URL (See the second usage example above... changing scriptid to your current numerical scriptid). This ensures that other Greasemonkey clones are following userscripts.org guidelines for update checking. This helps to prevent DoS and DDoS attacks for those clones that may implement script updating improperly.
NOTE: Some upstream releases of Greasemonkey may not be properly detecting the userscripts.org @updateURL and may be using a special CDN to intercept some of your daily update checks. If you wish to bypass this remote domain by restoring proper usage of the meta.js routine on userscripts.org please use this remote forks repository with the following git commands, or if available in the files section of this project. Remember to manually check for updates in Firefox otherwise next release will automatically be from upstream.:
$ git clone git://git.code.sf.net/p/greasemonkey/code greasemonkey
$ cd greasemonkey
$ git checkout slave
$ ./build.sh official
Compatibility: Greasemonkey 0.9.12+
Compatibility: Greasemonkey 0.9.14+
Blocks: UserScript
Value: URL
Usage:
// @downloadURL https://www.example.com/script.user.js
or
// @downloadURL https://userscripts.org/scripts/source/scriptid.user.js
Some Userscripts contain other special keys in the metadata blocks.
These unsupported metadata keys are ignored by the Greasemonkey extension, but can be read by human beings or utilized by other code and site configurations.
Keys
Properties
Blocks: UserScript / UserLibrary
Value: String
Usage:
// @copyright Year, Author (Author Website)
// @copyright 2009+, John Doe (https://www.example.com/projecthomepage)
Blocks: UserScript / UserLibrary
Value: String
Usage:
// @license License Type; License Homepage
// @license CC-BY-NC-SA-4.0; https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode
// @license GPL-3.0-or-later; https://www.gnu.org/licenses/gpl-3.0.txt
Blocks: UserScript
Value: URI
Usage:
// @supportURL https://example.com/mysupport
Status: End of Life 2015 08 24
Value: String
Usage:
// @oujs:author Username
Status: End of Life 2015 08 24
Value: String
Usage:
// @oujs:collaborator Username
Blocks: UserScript
Value: String or Number
Usage:
// @uso:script scriptid
https://userscripts.org/scripts/source/scriptid.meta.js
Blocks: UserScript
Value: String or Number
Usage:
// @uso:version versionid
https://userscripts.org/scripts/source/scriptid.meta.js
Blocks: UserScript
Value: String
Usage:
// @uso:timestamp Wed, 17 Jun 2009 22:50:26 +0000
https://userscripts.org/scripts/source/scriptid.meta.js
Blocks: UserScript
Value: String
Usage:
// @uso:hash 30d54f8ba24c0c6ec5710866e9b0839b2066a815
https://userscripts.org/scripts/source/scriptid.meta.js
Blocks: UserScript
Value: String or Number
Usage:
// @uso:rating 4.00
https://userscripts.org/scripts/source/_scriptid_.meta.js
Blocks: UserScript
Value: String or Number
Usage:
// @uso:installs count
// @uso:reviews count
// @uso:discussions count
// @uso:fans count
https://userscripts.org/scripts/source/scriptid.meta.js
Blocks: UserScript
Value: String
Usage:
// @uso:unlisted
Blocks: UserScript
Value: String
Usage:
// @attribution Attribution Name (Attribution Script Homepage)
// @attribution Jane Doe (https://www.example.com/janedoe/scriptid)
// @attribution Jack Doe (https://www.example.com/jackdoe/scriptid)
// @attribution Jill Doe (https://www.example.com/jilldoe/scriptid)
Blocks: UserScript
Value: String
Usage:
// @contributor Contributor Name (Contributor Homepage)
// @contributor Jane Doe (https://www.example.com/janedoe)
// @contributor Jack Doe (https://www.example.com/jackdoe)
// @contributor Jill Doe (https://www.example.com/jilldoe)
Blocks: OpenUserJS
Value: String
Usage:
// @collaborator Marti
Blocks: OpenUserJS
Value: String
Usage:
// @unstableMinify A brief technical reason
Blocks: UserScript
Value: Number or String
Usage:
// @major 0
Blocks: UserScript
Value: Number or String
Usage:
// @minor 0
Blocks: UserScript
Value: Number or String
Usage:
// @build 1
⬆ ⬇ | Multiplexing Metadata Blocks | Knowing Your Own Metadata | jQuery require
// ==UserScript==
// @name My Script
// @namespace https://www.example.com/gmscripts
// @description Scripting is fun
// @include https://www.example.com/*
// @include https://www.example.org/*
// @exclude https://www.example.org/foo
// @require foo.js
// @resource resourceName1 resource1.png
// @resource resourceName2 https://www.example.com/resource2.png
// ==/UserScript==
// ==OpenUserJS==
// @author sizzle
// @collaborator Marti
// ==/OpenUserJS==
Some sites require mirroring certain keys in multiple blocks. OpenUserJS.org is an example. Take their "Getting Started with a User Library" example.
Typically one can write this as two separate blocks like this:
// ==UserScript==
// @namespace https://openuserjs.org/users/username
// @exclude *
// @name Getting Started with a User Library
// @description Showing the current basic and recommended format for a Library script
// @copyright 2017, User Name (https://openuserjs.org/users/username)
// @license MIT
// @version 0.0.0
// ==/UserScript==
// ==UserLibrary==
// @name Getting Started with a User Library
// @description Showing the current basic and recommended format for a Library script
// @copyright 2017, User Name (https://openuserjs.org/users/username)
// @license MIT
// @version 0.0.0
// ==/UserLibrary==
... however a more convenient method to keep these keys syncronized between the blocks is to do this:
// ==UserScript==
// @namespace https://openuserjs.org/users/username
// @exclude *
// ==UserLibrary==
// @name Getting Started with a User Library
// @description Showing the current basic and recommended format for a Library script
// @copyright 2017, User Name (https://openuserjs.org/users/username)
// @license MIT
// @version 0.0.0
// ==/UserScript==
// ==/UserLibrary==
Logical Control Flow
Methodology
Preparation | Restructuring | Implementation |
---|---|---|
Starting a restructure | with Function Expression | Finishing a restructure |
with E4X XMLList | ||
with ES6+ Template Literals | ||
with GM_info |
Begin by including this L/GPL 3.0 or later code snippet in your source which analyzes the native UserScript Metadata Block and returns a restructured Object in the JSON compatible format.
function parseHeaders(metadataBlock) {
metadataBlock = metadataBlock.toString();
var re = /^\/\/ @(\S+)(?:\s+(.*))?/;
var headers = {};
var name, prefix, header, key, value;
var lines = metadataBlock.split(/[\r\n]+/).filter(function (e, i, a) {
return (e.match(re));
});
for (var line in lines) {
[, name, value] = lines[line].replace(/\s+$/, "").match(re);
switch (name) {
case "licence":
name = "license";
break;
}
[key, prefix] = name.split(/:/).reverse();
if (key) {
if (prefix) {
if (!headers[prefix])
headers[prefix] = new Object;
header = headers[prefix];
}
else
header = headers;
if (header[key]) {
if (!(header[key] instanceof Array))
header[key] = new Array(header[key]);
header[key].push(value || "");
}
else
header[key] = value || "";
}
}
if (headers["license"])
headers["licence"] = headers["license"];
return headers;
}
Encase the UserScript Metadata Block in a Function Expression (FE) and pass to the parsing routine.
var fileMETA = parseHeaders((function () {
// ==UserScript==
// @name My Script
// @namespace https://www.example.com/gmscripts
// @description Scripting is fun
// @copyright 2009+, John Doe (https://www.example.com/~jdoe)
// @license GPL-3.0-or-later; https://www.gnu.org/licenses/gpl-3.0.txt
// @version 0.0.1
// @include https://www.example.com/*
// @include https://www.example.org/*
// @exclude https://www.example.org/foo
// @require foo.js
// @resource resourceName1 resource1.png
// @resource resourceName2 https://www.example.com/resource2.png
// @grant GM_log
// @grant GM_xmlhttpRequest
// @uso:script scriptid
// ==/UserScript==
}));
Encase the UserScript Metadata Block in a CDATA tag group and pass to the parsing routine.
IMPORTANT: E4X has been deprecated and disabled on Mozilla Firefox 17+ versions and is slated to be removed by version 18 release.
If a user omits a Greasemonkey supplied key such as @namespace then it will not be reflected in this copy.
var fileMETA = parseHeaders(<><![CDATA[
// ==UserScript==
// @name My Script
// @namespace https://www.example.com/gmscripts
// @description Scripting is fun
// @copyright 2009+, John Doe (https://www.example.com/~jdoe)
// @license GPL-3.0-or-later; https://www.gnu.org/licenses/gpl-3.0.txt
// @version 0.0.2
// @include https://www.example.com/*
// @include https://www.example.org/*
// @exclude https://www.example.org/foo
// @require foo.js
// @resource resourceName1 resource1.png
// @resource resourceName2 https://www.example.com/resource2.png
// @grant GM_log
// @grant GM_xmlhttpRequest
// @uso:script scriptid
// ==/UserScript==
]]></>.toString()
);
Encase the UserScript Metadata Block in back apostrophes, a.k.a back ticks, to treat it as a multi-line text block.
var fileMETA = parseHeaders(`
// ==UserScript==
// @name My Script
// @namespace https://www.example.com/gmscripts
// @description Scripting is fun
// @copyright 2009+, John Doe (https://www.example.com/~jdoe)
// @license GPL-3.0-or-later; https://www.gnu.org/licenses/gpl-3.0.txt
// @version 0.0.3
// @include https://www.example.com/*
// @include https://www.example.org/*
// @exclude https://www.example.org/foo
// @require foo.js
// @resource resourceName1 resource1.png
// @resource resourceName2 https://www.example.com/resource2.png
// @grant none
// @uso:script scriptid
// ==/UserScript==
`
);
The last known native UserScript Metadata Block is available with GM_info. Retrieve this String and pass to the parsing routine.
GM_info.script
may have some scoping issues with certain Object types such as iterating over the @resource values contained in GM_info.script.resources
with for…in
// ==UserScript==
// @name My Script
// @namespace https://www.example.com/gmscripts
// @description Scripting is fun
// @copyright 2009+, John Doe (https://www.example.com/~jdoe)
// @license GPL-3.0-or-later; https://www.gnu.org/licenses/gpl-3.0.txt
// @version 0.0.4
// @include https://www.example.com/*
// @include https://www.example.org/*
// @exclude https://www.example.org/foo
// @require foo.js
// @resource resourceName1 resource1.png
// @resource resourceName2 https://www.example.com/resource2.png
// @grant GM_log
// @grant GM_xmlhttpRequest
// @uso:script scriptid
// ==/UserScript==
var fileMETA = parseHeaders(
GM_info.scriptMetaStr
/* These methods are optional and mutually inclusive for tidiness */
.trim().split(/\n/).map(function (e) { return e.trim(); }).join("\n")
);
Using GM_xmlhttpRequest to retrieve the meta.js routine from userscripts.org⁸⁰⁸⁰⋔ implement and display name/value pairings to the console.
GM_xmlhttpRequest({
method:"GET",
url:"https://userscripts.org/scripts/source/" + fileMETA["uso"]["script"] + ".meta.js",
headers:{
"Accept":"text/javascript; charset=UTF-8"
},
overrideMimeType:"application/javascript; charset=UTF-8",
onload:function(response) {
var httpsMETA = parseHeaders(response.responseText);
GM_log([
"\n---------- Local ----------",
fileMETA["name"] + " version " + fileMETA["version"],
fileMETA["copyright"],
fileMETA["license"],
fileMETA["description"],
fileMETA["include"],
fileMETA["exclude"],
"\n---------- Remote ----------",
httpsMETA["name"] + " version " + httpsMETA["version"],
httpsMETA["copyright"],
httpsMETA["license"],
httpsMETA["description"],
httpsMETA["include"],
httpsMETA["exclude"],
httpsMETA["uso"]["script"],
httpsMETA["uso"]["version"],
httpsMETA["uso"]["timestamp"],
httpsMETA["uso"]["hash"],
httpsMETA["uso"]["installs"],
httpsMETA["uso"]["reviews"],
httpsMETA["uso"]["rating"],
httpsMETA["uso"]["discussions"],
httpsMETA["uso"]["fans"]
].join("\n"));
}
});
// ==UserScript==
// @name Hello jQuery
// @namespace https://www.example.com/examples
// @description jQuery test script
// @include *
// @require https://code.jquery.com/jquery-latest.js
// @grant none
// ==/UserScript==
/* jshint esversion: 5 */
/* globals $, jQuery */
this.$ = this.jQuery = jQuery.noConflict(true);
$(document).ready(function() {
$("a").click(function() {
alert('Hello world!');
});
});
Wiki: GM_getResourceText
Wiki: GM_getResourceURL
Wiki: GM_info
Wiki: Greasemonkey_Manual:API
Wiki: Greasemonkey_Manual:Creating_Scripts
Wiki: Greasemonkey_Manual:Environment
Wiki: Greasemonkey_Manual:Managing_Scripts
Wiki: Main_Page
Wiki: Metadata_Block
Wiki: Script_injection_rules
Wiki: Scripts_directory
Wiki: User_script
Wiki: Version_history#10
Wiki: XPCNativeWrapper
Wiki: config.xml