├── .bowerrc ├── .gitattributes ├── .gitconfig ├── .gitignore ├── README.md ├── bower.json ├── build.js ├── doc ├── api │ ├── GM_addStyle.html │ ├── GM_deleteValue.html │ ├── GM_getResourceText.html │ ├── GM_getResourceURL.html │ ├── GM_getValue.html │ ├── GM_info.html │ ├── GM_listValues.html │ ├── GM_log.html │ ├── GM_openInTab.html │ ├── GM_registerMenuCommand.html │ ├── GM_setClipboard.html │ ├── GM_setValue.html │ └── GM_xmlhttpRequest.html ├── intro │ ├── about.html │ └── gmScript.html └── tutorial │ ├── meta.html │ ├── other.html │ ├── publish.html │ └── warning.html ├── index.html ├── makefile ├── package.json ├── res ├── App.js └── App.scss └── tpl ├── api.html ├── arg.html ├── define.html ├── deprecated.html ├── meta.html └── snipset ├── gmAddStyle.html └── gmValue.html /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "cdn" 3 | } 4 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | filter=cr 2 | -------------------------------------------------------------------------------- /.gitconfig: -------------------------------------------------------------------------------- 1 | [filter "cr"] 2 | clean = tr '\\r' '\\n' 3 | smudge = tr '\\n' '\\r' 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | node_modules 3 | 4 | # CDN needs to be downloaded separately 5 | cdn/* 6 | 7 | build.log 8 | .idea 9 | 10 | res/App.min.js 11 | res/App.css -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 关于该项目 2 | 这是一个很有爱的中文 GreaseMonkey 用户脚本开发手册 :3 3 | 4 | 手册采用下述开源库进行开发: 5 | 6 | * Angular JS 7 | * ANgular UI (ui-router) 8 | * Bootstrap 9 | * jQuery 10 | 11 | 该项目于 [Firefox8](http://firefox8.qiniudn.com/doc/index.html) 获得灵感制作而成,其中 API 资料均从 [GM 官方百科](http://wiki.greasespot.net/)提取翻译而成。 12 | 13 | # 安装 14 | 首先需要安装 Bower。 15 | ```bash 16 | sudo npm install bower -g 17 | ``` 18 | 19 | 执行 `bower install` 安装所需第三方库 (Angular, Bootstrap, jQuery) 20 | 21 | 如果需要压缩小册子为 index + css + js 这三个文档的话执行 `make` 即可 (需要安装 closure-compiler、nodejs、npm、sass)。 22 | ```bash 23 | # Ubuntu 下安装依赖项 (未测试) 24 | sudo apt-get install libclosure-compiler-java nodejs npm ruby 25 | # 安装 sass 26 | sudo gem install sass 27 | # 安装构建用的库 28 | npm install 29 | ``` 30 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ucDevBook", 3 | "version": "1.0.0", 4 | "dependencies": { 5 | "angular": "~1.2.16", 6 | "angular-ui": "~0.4.0", 7 | "angular-ui-router": "~0.2.10", 8 | "bootstrap": "~3.1.1", 9 | "jquery": "~2.1.1", 10 | "jquery.scrollTo": "~1.4.12" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /build.js: -------------------------------------------------------------------------------- 1 | // Build Site, require node js. 2 | 3 | var fs = require ('fs'); 4 | var CleanCSS = require('clean-css'); 5 | var HTMLMin = require('html-minifier').minify; 6 | 7 | var outDir = './build/'; 8 | 9 | var indexFile = fs.readFileSync('./index.html').toString(); 10 | 11 | var rmComment = function (r) { 12 | return r.replace(//g, ''); 13 | }; 14 | 15 | var writeFile = function (writeTo, fileContent) { 16 | console.log ('Write to: %s', writeTo); 17 | fs.writeFileSync(outDir + writeTo, fileContent); 18 | }; 19 | 20 | var minHTML = function (HTML) { 21 | return HTMLMin (HTML.toString(), { 22 | removeComments: true, 23 | removeCommentsFromCDATA: true, 24 | removeCDATASectionsFromCDATA: true, 25 | collapseWhitespace: true, 26 | conservativeCollapse: true, 27 | collapseBooleanAttributes: true, 28 | // removeAttributeQuotes: true, 29 | removeRedundantAttributes: true, 30 | // removeEmptyAttributes: true, 31 | removeOptionalTags: true, 32 | // removeEmptyElements: true, 33 | keepClosingSlash: true, 34 | caseSensitive: true 35 | }).replace(/>\s+<'); 36 | }; 37 | 38 | var procDir = function (dirName) { 39 | var ret = ''; 40 | console.log ('Build directory: %s', dirName); 41 | fs.readdirSync (dirName).forEach (function (file) { 42 | var curPath = dirName + file; 43 | if (fs.lstatSync(curPath).isDirectory()) { 44 | ret += procDir (curPath + '/'); 45 | return ; 46 | } 47 | 48 | // Process file 49 | console.log ('Add file: %s', curPath); 50 | ret += ''; 52 | }); 53 | 54 | return ret ; 55 | }; 56 | 57 | // 构建 CSS 档 58 | writeFile ('index.html', minHTML(indexFile.replace (/<\!--CSS\:START-->[\s\S]+<\!--CSS\:END-->/g, function (z) { 59 | var css = ''; 60 | rmComment(z).replace (/href="(.+?)"/g, function (z, link) { 61 | css += fs.readFileSync (link).toString(); 62 | }); 63 | 64 | writeFile ('App.css', new CleanCSS().minify(css.replace(/\/\*[\s\S]*?\*\//g, ''))); 65 | return ''; 66 | }). 67 | 68 | // 构建 JS 档 69 | replace (/<\!--JS\:START-->[\s\S]+<\!--JS\:END-->/g, function (z) { 70 | var js = ''; 71 | rmComment(z).replace (/src="(.+?)"/g, function (z, src) { 72 | js += '\n;' + fs.readFileSync (src).toString(); 73 | }); 74 | 75 | writeFile ('App.d.js', 76 | js.slice(1) 77 | ); 78 | return ''; 79 | }). 80 | // 构建全部 HTML 页面到一个文件 81 | replace('', procDir ('./tpl/') + procDir ('./doc/')))); 82 | -------------------------------------------------------------------------------- /doc/api/GM_addStyle.html: -------------------------------------------------------------------------------- 1 |
该函数用于插入一段 CSS 到当前页面。它将插入一个新的 <style>
元素,添加输入的 CSS,然后插入到 <head>
末端。
GM_addStyle("body { color: white; background-color: black; } img { border: 0; }");18 | 19 |
!important
到 CSS 末尾可强行覆盖其属性值。该函数用于删除一个已经储存的值。
3 |如果你需要寻找如何设定值,请参见
undefined
删除一个叫做 foo 的值。
22 |GM_deleteValue("foo");23 | 24 |
删除指定数组外的数据外的值
25 |var GM_removeAndKeep = function (arrKeysToKeep) { 26 | if (!arrKeysToKeep instanceof Array) 27 | // 不是合法的数组参数 28 | return ; 29 | 30 | GM_listValues().forEach (function (key) { 31 | if (arrKeysToKeep.indexOf (key) == -1) { 32 | // 不需要保留,删掉 33 | GM_deleteValue (key); 34 | } 35 | }); 36 | }; 37 | 38 | // 删除 config、cache 外的数据 39 | GM_removeAndKeep (['config', 'cache']); 40 |41 | 42 |
该函数用于获取定义的 @resource
的元属性值。
String
当资源不存在的时候将抛出异常。
21 | 22 |// ==UserScript== 24 | // 首先定义一个资源 25 | // @resource prototype http://www.example.com/prototype.js 26 | // ==/UserScript== 27 | 28 | // 然后从这里获取资源地址 29 | var prototypeSource = GM_getResourceText("prototype");30 | -------------------------------------------------------------------------------- /doc/api/GM_getResourceURL.html: -------------------------------------------------------------------------------- 1 |
该函数用于获取定义的 @resource
所指向的内容。
String
greasemonkey-script:[脚本唯一识别码]/[资源名]19 | 如 20 |
greasemonkey-script:94242686-1400-4dce-982a-090cbfef7ba1/image21 | 22 | 23 |
GM 1.0 后返回的数据为 base64 编码过的 data:
协议地址。
该地址可直接用于任何支援 data:
协议的元素 (如: <img>
)。但是在 <object>
和 <embed>
下无效。
做出该项更改的原因是 Base64 编码的地址在这里被重新编码了,因为 Base64 使用了 +
和 /
这两个特殊字符。
因此,你可能需要使用 decodeURIComponent
将取得的 Base64 还原一次。
当资源不存在的时候将抛出异常。
31 | 32 |// ==UserScript== 34 | // 首先定义一个资源 35 | // @resource logo http://www.example.com/logo.png 36 | // ==/UserScript== 37 | 38 | var img = document.createElement('img'); 39 | // 将取得的地址作为图像地址并插入到页面 40 | img.src = GM_getResourceURL("logo"); 41 | document.body.appendChild(img);42 | -------------------------------------------------------------------------------- /doc/api/GM_getValue.html: -------------------------------------------------------------------------------- 1 |
该函数用于获取脚本之前使用 String
、Boolean
等类型。
该函数将返回获取到的值,如果不存在则返回 default
传入的值。
抓取一个叫做 foo 的储存值
27 |// 输出储存为 foo 的值或 undefined 28 | 29 | GM_log(GM_getValue("foo"));30 | 31 |
抓取一个叫做 'timezoneOffset' 的值并提供默认值:
32 |// 如果该值不存在 GM_getValue() 则会返回 +8 (整数) 33 | 34 | GM_log(GM_getValue("timezoneOffset", +8));35 | 36 |
和其它 API 不同,这是一个变量而非函数。
3 |该变量提供当前执行的用户脚本的元数据。
4 | 5 |GM_info7 | 8 |
该对象含有下述属性:
10 |@match
,可能为空数组 (0.9.16 下有问题)true
,GM 将尝试自动更新脚本。0.9.16
该函数用于获取所有被赋值的值名,作为一个文本数组返回。
3 | 4 |Array
: 文本数组
输出所有储存的值名
13 |GM_log(GM_listValues());14 | 15 | -------------------------------------------------------------------------------- /doc/api/GM_log.html: -------------------------------------------------------------------------------- 1 |
一个简单的日志函数,用于输出数据至控制台。按下键盘 F12
查看。
输出数据将输出至「错误控制台」,包括所属脚本命名空间以及传入的文本。
10 | 11 |因此,下述脚本:
12 | 13 |// ==UserScript== 14 | // @name GM_log Example 15 | // @namespace http://www.example.com/ 16 | // ==/UserScript== 17 | 18 | GM_log("This is an example of GM_log");19 | 20 |
将产生这样的输出:
21 |http://www.example.com/GM_log Example: This is an example of GM_log22 | 23 |
.toString ()
方法的变量。
34 | undefined
GM_log("Hello, World!"); 41 | GM_log("Warning, " + someInputField.value + "!");-------------------------------------------------------------------------------- /doc/api/GM_openInTab.html: -------------------------------------------------------------------------------- 1 |
在新标签页开启指定地址。
3 | 4 |true
,即后台开启。
21 | 0.8.2 之前: undefined
25 | 0.8.2 之后: 新标签的 window
对象
GM_openInTab("http://www.example.com/"); 29 | GM_openInTab("http://www.example.com/", false); // 打开并切换到目标标签页 30 |31 | -------------------------------------------------------------------------------- /doc/api/GM_registerMenuCommand.html: -------------------------------------------------------------------------------- 1 |
该函数允许用户脚本添加菜单到用户脚本指令。
3 | 4 |undefined
GM_registerMenuCommand("Hello, world (simple)", function () { 31 | /// ... 32 | }); 33 | 34 | var yooooo = function () { 35 | // ... 36 | }; 37 | GM_registerMenuCommand("Hello, world!", yooooo, "h"); 38 |39 | -------------------------------------------------------------------------------- /doc/api/GM_setClipboard.html: -------------------------------------------------------------------------------- 1 |
更改当前剪切版内容为参数内容。
3 | 4 |GM_setClipboard('http://www.example.com/short-url-code');-------------------------------------------------------------------------------- /doc/api/GM_setValue.html: -------------------------------------------------------------------------------- 1 |
该函数用于写入一些数据并储存,可使用 String
、Boolean
等类型。
undefined
将名为 foo
的项目储存为 bar
GM_setValue("foo", "bar");28 | 29 |
该函数主要用于实现跨域传输信息调用,如从 www.example.com
请求 upload.example.com
的内容。
请求方式,最常见的有 GET
与 POST
两种。
也有其它的请求方式,详细请翻阅 w3 标准。
21 |false
。当为 true
的时候将使用 .sendAsBinary()
方法。
33 | context
。
38 | true
时,请求结束前将导致浏览器界面无项应。该模式下返回值即为返回数据。
63 | onabort
、onerror
、onload
、onprogress
这四个上传回调函数的对象。
74 | text/html; charset=GBK
80 | 回调参数将会被传入一个参数,请参考下方的「请求对象」。
87 |可用的回调参数:
88 |所有的回调参数调用时都会传入一个变量。根据回调的不同,下述数据可能不全部可用。
101 |404
404 Not Found
其中,GM 还引用了自己独有的属性值:
109 |context
属性。
115 | 而针对 progress
的进度回调,传参包含下述属性:
在默认(异步请求, asynchronous)请求方式下,将返回一个带有 abort()
方法的对象。
在同步请求方式下,将返回一个带有 abort()
及下述属性的对象:
GM_xmlhttpRequest({ 146 | method: "GET", 147 | url: "http://www.example.com/", 148 | onload: function(response) { 149 | alert(response.responseText); 150 | } 151 | });152 | 153 |
GM_xmlhttpRequest({ 155 | method: "GET", 156 | url: "http://www.example.net/", 157 | headers: { 158 | "User-Agent": "Mozilla/5.0", // 如果未指定则使用浏览器默认值. 159 | "Accept": "text/xml" // 如果未指定则教给浏览器自行判断 160 | }, 161 | onload: function(response) { 162 | var responseXML = null; 163 | // 插入 responseXML 到现有对象 (仅限于 XML 对象) 164 | if (!response.responseXML) { 165 | responseXML = new DOMParser() 166 | .parseFromString(response.responseText, "text/xml"); 167 | } 168 | 169 | GM_log([ 170 | response.status, 171 | response.statusText, 172 | response.readyState, 173 | response.responseHeaders, 174 | response.responseText, 175 | response.finalUrl, 176 | responseXML 177 | ].join("\n")); 178 | } 179 | });180 | 181 |
当发出 POST 请求时,大多数网站都要求 Content-Type
头部设定为 application/x-www-form-urlencoded
才允许提交。
GM_xmlhttpRequest({ 184 | method: "POST", 185 | url: "http://www.example.net/login", 186 | data: "username=johndoe&password=xyz123", 187 | headers: { 188 | "Content-Type": "application/x-www-form-urlencoded" 189 | }, 190 | onload: function(response) { 191 | if (response.responseText.indexOf("Logged in as") > -1) { 192 | location.href = "http://www.example.net/dashboard"; 193 | } 194 | } 195 | });196 | 197 |
如同 HTTP 的定义,你可以发出一个 HEAD 请求只获取网页的头部信息。
200 |GM_xmlhttpRequest({ 201 | url: "http://www.example.com", 202 | method: "HEAD", 203 | onload: function(response) { 204 | GM_log(response.responseHeaders); 205 | } 206 | });207 | -------------------------------------------------------------------------------- /doc/intro/about.html: -------------------------------------------------------------------------------- 1 |
这是一个很有爱的中文 GreaseMonkey 用户脚本开发手册,托管于 GitHub :3
3 | 4 |该项目使用下述开源库进行开发,在此表示感谢:
5 |该项目于 Firefox8 获得灵感制作而成,其中 API 资料均从 GM 官方百科提取翻译而成。
14 | -------------------------------------------------------------------------------- /doc/intro/gmScript.html: -------------------------------------------------------------------------------- 1 |从原理上来说,GreaseMonkey(GM) 脚本相当于在网页上插入一段 JavaScript(JS) 脚本。
2 | 3 |因此,它的编写语言本质上就是 JS。JS 所能实现的功能,GM 脚本都能实现。但 GM 它的强大并不限与此,还能实现如跨域请求、更改剪辑版等原生 JS 无法实现的功能。
4 | 5 | -------------------------------------------------------------------------------- /doc/tutorial/meta.html: -------------------------------------------------------------------------------- 1 |元数据 包含了用户脚本的元数据。一般包含脚本名称、命名空间、执行地址和不执行的地址。
3 |这些数据一般为 JavaScript 注释形式储存于脚本顶部。
4 | 5 |// ==UserScript== 7 | // @name 我的第一个脚本 8 | // @namespace http://www.example.com/gmscripts 9 | // @description 看我突破天际的钻头 10 | // @include http://www.example.com/* 11 | // @include http://www.example.org/* 12 | // @exclude http://www.example.org/foo 13 | // @require https://example.com/foo.js 14 | // @resource logo logo.png 15 | // @resource yooo http://www.example.com/resource2.png 16 | // @version 1.0 17 | // @icon http://www.example.net/icon.png 18 | // ==/UserScript==19 | 20 |
元数据必须按照以下格式填写
22 |// ==UserScript== 23 | // @属性名 属性值 24 | // ==/UserScript==25 | 26 |
每一条属性必须使用双斜杠//
开头,不得使用块注释/* */
。
28 | 与此同时,所有的脚本元数据必须放置于
29 | // ==UserScript==
和 // ==/UserScript==
30 | 之间才会被认定为有效的元数据。
31 |
元数据名和值中间可以为了保持美观添加多个空格。
34 | 35 |用于指示该脚本名称,将显示于「脚本管理」及「猴子菜单」。该值同时为相同命名空间的唯一识别码。
41 |如果未指定该参数,将尝试从文件名获取。
42 |简单描述当前脚本的功能。也会于用户脚本管理界面显示。
47 |命名空间和脚本名称是 GM 用于识别脚本唯一性的方法。如果两个脚本的名称和命名空间均相同,那么后安装的脚本将覆盖之前安装的脚本。
52 |一般脚本作者将其项目使用相同命名空间,然后每个项目使用不同名称。
53 |同时因为命名空间没有语法限制,一些作者直接使用项目首页作为其命名空间。该值只要不与他人重复即可。
54 |指定脚本的版本号,GM 用来判断脚本是否更新的依据。
59 |引用一个外部链接的脚本作为库使用。最常见的外部库为 jQuery。
65 |因为 GM 对 IE 的支援不佳,因此一般不用考虑 IE 的兼容性,放心去搞吧~
66 |* GM 的默认设定要求该值使用 https://
协议。
该值用于指定发现更新后使用的脚本地址。若未指定则使用安装脚本地址。
72 |用于检查更新使用的地址。该地址应只包含元数据而不包含脚本内容。
77 |指定脚本所请求的权限,如 unsafeWindow
用于访问浏览器的 window
对象。
其它可选值则为 GM 提供的 GM_
开头的 API。
// ==UserScript== 88 | // @include http://www.example.com/foo/* 89 | // @include http://www.example.org/*.bar 90 | // @exclude http://www.example.com/foo/baz 91 | 92 | // GM 0.9.8 开始, @include 允许使用正则表达式匹配 93 | // @include /^https?://www\.example\.com/.*$/ 94 | // @include /^http://www\.example\.(org|net)// 95 | // ==/UserScript==96 |
@include
和 @exclude
使用 *
表示任意字符,或标准正则表达式对象。
同时,它还支持一个特殊的匹配符,.tld
。
@include http://www.example.tld/*99 |
请注意: 如果使用 tld 匹配请务必确保数据不会被泄露给无关网站。
100 |其中, @exclude
的匹配权限比 @include
要高。
GM 根据 Chrome 的 Match Patterns 实现的另外一种匹配方案,比 @include / @exclude
的匹配更严格。详细请参考上述 Chrome 开发者页面。
用于脚本管理界面显示的图标。虽然什么图片都可以,但建议使用 32x32
大小的图标。
该参数可指定任意数量的资源。但请注意,同一脚本下资源名不得重复。
118 |资源将在安装脚本的时候下载一次,之后不会进行更新。
119 |这些资源可以稍后通过
@resource
的值被更改后将尝试重新下载。该值用于指定脚本执行的时机,可用的参数只能为 document-start
和 document-end
两种。
Chrome 下的 TamperMonkey 还提供了 document-body 这一选项,但是 GM 官方文档找不到说明,最好避免使用。
129 |如果不填写该值,GM 将采用 document-end
作为默认值。
检查脚本是否执行于 document-start
,检查 document.readyState 的值即可:
if ('loading' == document.readyState) { 133 | alert("脚本执行于 document-start。"); 134 | } else { 135 | alert("脚本当前的 document.readyState: " + document.readyState); 136 | }137 |
该功能在 GM 1.0+ 已经被抛弃使用,后期的 GM 默认启用该功能。
142 |// @unwrap143 |
该元数据表示代码将不会使用 (function () { /* 代码 */ })();
这样的形式执行,因此请注意不要在外部填写 return
语句。
简单来说有以下几点:
24 |Gist 是 GitHub 提供的代码分享服务。Gist 文件可以重命名为 .user.js
结尾,并通过分享「Raw Link」实现一键安装脚本。
该站默认使用 HTTPS 协议。
5 | 6 |由 UserStyles 维护者开发,默认使用 HTTPS 协议。
9 |其网站源码托管于 GitHub。
10 | 11 |「一个使用 Node.js 构建的用户脚本源」。仅限 HTTPS 协议,其网站源码也托管于 GitHub。
14 | 15 |「Monkey Guts 是一个全新的脚本源」。仅限 HTTPS 协议,网站目前处于闭源状态。
18 | 19 |规模最大以及运行时间最长的用户脚本托管站点。HTTPS 可用但默认未启用。从 2014 年开始,因年久是修导致关站。
22 | 23 |只要你的地址结尾为 .user.js
并能正常访问就能使用 GM 一键安装了。
在最初的 GM 插件设计中,GM 脚本被直接插入到页面并提供了 GM_ 系列函数的访问权限。
3 |2005 年 7月,Mark Pilgrim 发现了 一个安全漏洞 —— 运行 GM 脚本的网站可以直接调用 GM_ 系列函数。
4 |为了填补这一漏洞,GM 团队更改了设计理念,使用匿名函数包装脚本后执行,并提供 unsafeWindow
接口用于访问网页变量。
function {{foo}} ( 2 | 无参数 3 | {{$index?', ':''}}{{arg}} 4 | )
-------------------------------------------------------------------------------- /tpl/arg.html: -------------------------------------------------------------------------------- 1 |{{ type }}
{{ type }}
// @{{key}} {{code}}5 | 6 |
不使用 API 实现该功能:
2 |// 返回插入的 style 元素 3 | var myAddStyle = function (css) { 4 | var ret = document.createElement ('style'); 5 | ret.textContent = css; 6 | (document.head || document.getElementsByTagName ('head')[0]).appendChild (ret); 7 | return ret; 8 | }-------------------------------------------------------------------------------- /tpl/snipset/gmValue.html: -------------------------------------------------------------------------------- 1 |
储存、写入对象数据:
2 |var GM_setObject = function (name, value) { 3 | if (value instanceof Object) { 4 | // 使用 JSON.stringify 将值转换为文本。 5 | GM_setValue (name, JSON.stringify (value)); 6 | } 7 | }, GM_getObject = function (name, default) { 8 | try { 9 | return JSON.parse (GM_getValue (name, '') || '{}'); 10 | } catch (e) { 11 | // 如果抓取的数据有误报错就直接返回默认值。 12 | return default; 13 | } 14 | };15 | 16 |
抓取所有储存的数据至对象数组:
17 |var getAllKey = function () { 18 | var ret = {}; 19 | GM_listValues().map(function (thing) { 20 | ret[thing] = GM_getValue (thing); // 值如果为对象数据需要手动调用 JSON.parse 解析。 21 | }); 22 | };23 | 24 |
清空所有储存的数据:
25 |GM_listValues().map(GM_deleteValue);26 | --------------------------------------------------------------------------------