Initial commit (code only without large binaries)

This commit is contained in:
robin
2026-02-15 18:58:44 +08:00
commit 35df75498f
9442 changed files with 1495866 additions and 0 deletions

26
EdgeUser/web/docs/@toc.md Normal file
View File

@@ -0,0 +1,26 @@
* CDN
* [HTTP](cdn/http.md)
* [HTTPS](cdn/https.md)
* [HTTP/3](cdn/http3.md)
* [WebP](cdn/webp.md)
* [访问鉴权](cdn/http-auth.md)
* [URL鉴权方式A](cdn/http-auth-type-a.md)
* [URL鉴权方式B](cdn/http-auth-type-b.md)
* [URL鉴权方式C](cdn/http-auth-type-c.md)
* [URL鉴权方式D](cdn/http-auth-type-d.md)
* [URL跳转](cdn/redirect.md)
* [防盗链](cdn/referers.md)
* [UA名单](cdn/user-agent.md)
* [CC防护](cdn/cc.md)
* [访客IP地址](cdn/remote-addr.md)
* [Websocket](cdn/websocket.md)
* [HTTP报头](cdn/http-header.md)
* [CORS跨域设置](cdn/cors.md)
* [PROXY Protocol](cdn/proxy-protocol.md)
* [IPv6](cdn/ipv6.md)
* [重写规则](cdn/rewrite.md)
* [请求变量](cdn/variables.md)
* [常见问题](cdn/qa.md)
* WAF
* [国家/地区封禁](cdn/waf/deny-countries.md)
* [省份封禁](cdn/waf/deny-provinces.md)

View File

@@ -0,0 +1,22 @@
# CC防护
## 简介
高级版本的CC防护功能可以用来快速开启CC防护功能。
## 设置
可以在网站“设置” -- “CC防护”中开启
![](cc1.png)
点开“更多选项”:
![](cc2.png)
其中:
* `启用CC无感防护` - 是否启用CC防护启用后自动检测并拦截CC攻击
* 更多选项
* `单IP最低QPS` - 当某个IP在1分钟内平均QPS达到此值时才会触发CC防护如果设置为0表示任何访问都会触发
* `例外URL` - 如果填写了例外URL表示这些URL跳过CC防护不做处理比如API类的调用不做限制可以填入 `/api/*`
* `限制URL` - 如果填写了支持URL表示只对这些URL进行CC防护处理如果不填则表示支持所有的URL比如只想针对搜索URL进行CC防护可以填入 `/search*`
* `检查请求来源指纹` - 在接收到HTTPS请求时尝试检查请求来源的指纹用来检测代理服务和爬虫攻击如果你在网站前面放置了别的反向代理服务请取消此选项
* `启用GET302校验` - 选中后表示自动通过GET302方法来校验客户端适用于网页类的应用不适用于API应用
* `使用自定义阈值` - 从v1.2.7版本开始)是否使用自定义阈值,默认情况下,会使用系统内置的规则,如果想自己修改,可以选中此选项,并根据自己的需要填入各个时间范围内的请求数限制

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB

View File

@@ -0,0 +1,37 @@
# CORS跨域设置
## 问题
在跨域访问网站时经常会遇到被CORS策略拦截问题浏览器控制台上会出现类似于以下的提示
~~~
Access to XMLHttpRequest at 'http://example.com/post' from origin 'http://example.com:2345' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
~~~
## 解决方案
在对应网站"设置"中 -- "HTTP报头"设置 -- "响应报头"中修改"CORS自适应跨域"
![](cors1.png)
点击上图中的"修改"按钮,弹出窗口:
![](cors2.png)
选中"启用CORS自适应跨域",并"保存",其他选项通常不需要修改,界面会出现"已启用"
![](cors3.png)
这样在客户端访问网站时响应报头会自动增加以下内容:
~~~yaml
Access-Control-Allow-Credentials: ...
Access-Control-Allow-Methods: ...
Access-Control-Allow-Origin: ...
Access-Control-Expose-Headers: ...
Access-Control-Request-Method: ...
~~~
CORS跨域设置中更多选项说明除非你对CORS策略非常了解否则不要轻易修改这些选项
* `允许的请求方法列表` - `Access-Control-Allow-Methods`值设置。所访问资源允许使用的方法列表,不设置则表示默认为`PUT``GET``POST``DELETE``HEAD``OPTIONS``PATCH`
* `预检结果缓存时间` - `Access-Control-Max-Age`值设置。预检结果缓存时间0或者不填表示使用浏览器默认设置。注意每个浏览器有不同的缓存时间上限
* `允许服务器暴露的报头` - `Access-Control-Expose-Headers`值设置。允许服务器暴露的报头,请注意报头的大小写
* `实际请求方法` - `Access-Control-Request-Method`值设置。实际请求服务器时使用的方法,比如`POST`
* `仅OPTIONS有效` - 选中后表示当前CORS设置仅在OPTIONS方法请求时有效其他方法响应报头中不会出现这些跨域相关报头
## 常见问题
### 为什么直接在响应报头中添加 `Access-Control-Allow-Origin: *` 仍然会提示错误?
因为在请求响应交互过程中有Cookie等验证信息时浏览器来源站点列表`Access-Control-Allow-Origin`)中不允许使用星号(`*`)通配符,所以在任何情况下请使用本文中"CORS自适应跨域功能",会自动处理这种情况。

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

View File

@@ -0,0 +1,37 @@
# URL鉴权方式A
## 示例URL
~~~
https://example.com/images/test.jpg?sign=1661824870-c6d1a57067b21f7b-0baac47b6c2ad519bb1bfe7babff37a3
~~~
## URL生成规则
~~~
(http|https)://域名/文件路径?sign=时间戳-随机字符串-Hash值
~~~
其中:
* `sign` - 为默认的签名参数,可以自行修改
* `域名` - 保留原来的域名,比如 `example.com`
* `/文件路径` - 文件路径包含了最前面的斜杠(`/`)符号,比如 `/images/test.jpg`
* `时间戳` - Unix时间戳单位是秒比如 `1661824870`
* `随机字符串` - 你自定义的随机字符串,只能包含字母、数字,比如 `c6d1a57067b21f7b`,也可以是一个固定的值,比如 `hello`,但固定值会降低安全性
* `Hash值` - 目前只支持MD5 Hash算法具体为
~~~~javascript
md5("/文件路径@时间戳@随机字符串@密钥")
~~~~
其中密钥是你在创建鉴权方式时生成或填写的鉴权密钥;各个字段使用 `@` 符号连接,且文件路径前的斜杠(`/`)别忘记填写,也不要重复填写;以密钥为 `123456` 为例:
~~~~javascript
md5("/images/test.jpg@1661824870@c6d1a57067b21f7b@123456") // => 0baac47b6c2ad519bb1bfe7babff37a3
~~~~
结果需要是一个32位的仅包含小写字母和数字的字符串如果你使用的算法结果是大写的请转换为小写的
## 回源规则
查找缓存和回源规则时,会自动移除 `sign` 参数
## 常见问题
### URL中可以添加其他参数吗
URL中可以添加其他参数比如
~~~
https://example.com/images/test.jpg?sign=1661824870-c6d1a57067b21f7b-0baac47b6c2ad519bb1bfe7babff37a3&v=1&from=google
~~~
其中 `v=1&from=google` 参数是自定义参数,可以正确回源到源站。

View File

@@ -0,0 +1,35 @@
# URL鉴权方式B
## 示例URL
~~~
https://example.com/1661824870/64bf8671521f2a61a3b64691fde82729/images/test.jpg
~~~
## URL生成规则
~~~
(http|https)://域名/时间戳/Hash值/文件路径
~~~
其中:
* `域名` - 保留原来的域名,比如 `example.com`
* `/文件路径` - 文件路径包含了最前面的斜杠(`/`)符号,比如 `/images/test.jpg`
* `时间戳` - Unix时间戳单位是秒比如 `1661824870`
* `Hash值` - 目前只支持MD5 Hash算法具体为
~~~~javascript
md5("/文件路径@时间戳@密钥")
~~~~
其中密钥是你在创建鉴权方式时生成或填写的鉴权密钥;各个字段使用 `@` 符号连接,且文件路径前的斜杠(`/`)别忘记填写,也不要重复填写;以密钥为 `123456` 为例:
~~~~javascript
md5("/images/test.jpg@1661824870@123456") // => 64bf8671521f2a61a3b64691fde82729
~~~~
结果需要是一个32位的仅包含小写字母和数字的字符串如果你使用的算法结果是大写的请转换为小写的
## 回源规则
查找缓存和回源规则时,会自动移除路径中的 `时间戳``Hash值`
## 常见问题
### URL中可以添加其他参数吗
URL中可以添加其他参数比如
~~~
https://example.com/1661824870/64bf8671521f2a61a3b64691fde82729/images/test.jpg?v=1&from=google
~~~
其中 `v=1&from=google` 参数是自定义参数,可以正确回源到源站。

View File

@@ -0,0 +1,35 @@
# URL鉴权方式C
## 示例URL
~~~
https://example.com/64bf8671521f2a61a3b64691fde82729/1661824870/images/test.jpg
~~~
## URL生成规则
~~~
(http|https)://域名/Hash值/时间戳/文件路径
~~~
其中:
* `域名` - 保留原来的域名,比如 `example.com`
* `时间戳` - Unix时间戳单位是秒比如 `1661824870`
* `/文件路径` - 文件路径包含了最前面的斜杠(`/`)符号,比如 `/images/test.jpg`
* `Hash值` - 目前只支持MD5 Hash算法具体为
~~~~javascript
md5("/文件路径@时间戳@密钥")
~~~~
其中密钥是你在创建鉴权方式时生成或填写的鉴权密钥;各个字段使用 `@` 符号连接,且文件路径前的斜杠(`/`)别忘记填写,也不要重复填写;以密钥为 `123456` 为例:
~~~~javascript
md5("/images/test.jpg@1661824870@123456") // => 64bf8671521f2a61a3b64691fde82729
~~~~
结果需要是一个32位的仅包含小写字母和数字的字符串如果你使用的算法结果是大写的请转换为小写的
## 回源规则
查找缓存和回源规则时,会自动移除路径中的 `Hash值``时间戳`
## 常见问题
### URL中可以添加其他参数吗
URL中可以添加其他参数比如
~~~
https://example.com/64bf8671521f2a61a3b64691fde82729/1661824870/images/test.jpg?v=1&from=google
~~~
其中 `v=1&from=google` 参数是自定义参数,可以正确回源到源站。

View File

@@ -0,0 +1,37 @@
# URL鉴权方式D
## 示例URL
~~~
https://example.com/images/test.jpg?sign=64bf8671521f2a61a3b64691fde82729&t=1661824870
~~~
## URL生成规则
~~~
(http|https)://域名/文件路径?sign=Hash值&t=时间戳
~~~
其中:
* `sign` - 为默认的签名参数,可以自行修改
* `t` - 为默认的时间戳参数,可以自行修改
* `域名` - 保留原来的域名,比如 `example.com`
* `/文件路径` - 文件路径包含了最前面的斜杠(`/`)符号,比如 `/images/test.jpg`
* `时间戳` - Unix时间戳单位是秒比如 `1661824870`
* `Hash值` - 目前只支持MD5 Hash算法具体为
~~~~javascript
md5("/文件路径@时间戳@密钥")
~~~~
其中密钥是你在创建鉴权方式时生成或填写的鉴权密钥;各个字段使用 `@` 符号连接,且文件路径前的斜杠(`/`)别忘记填写,也不要重复填写;以密钥为 `123456` 为例:
~~~~javascript
md5("/images/test.jpg@1661824870@123456") // => 64bf8671521f2a61a3b64691fde82729
~~~~
结果需要是一个32位的仅包含小写字母和数字的字符串如果你使用的算法结果是大写的请转换为小写的
## 回源规则
查找缓存和回源规则时,会自动移除 `sign``t` 参数
## 常见问题
### URL中可以添加其他参数吗
URL中可以添加其他参数比如
~~~
https://example.com/images/test.jpg?sign=64bf8671521f2a61a3b64691fde82729&t=1661824870&v=1&from=google
~~~
其中 `v=1&from=google` 参数是自定义参数,可以正确回源到源站。

View File

@@ -0,0 +1,32 @@
# 访问鉴权
可以使用访问鉴权功能,来限制用户对某些资源的访问,或者防止恶意用户盗刷资源,降低网站的无效带宽损耗。
* [URL鉴权方式A](http-auth-type-a.md)
* [URL鉴权方式B](http-auth-type-b.md)
* [URL鉴权方式C](http-auth-type-c.md)
* [URL鉴权方式D](http-auth-type-d.md)
## 设置
以"基本认证"鉴权为例,在网站"设置"--"访问鉴权"页面中,选择"启用鉴权",然后点击"添加鉴权方式"按钮,创建一个新的鉴权方式,比如"基本认证"
![http-auth-1.png](http-auth-1.png)
这样,当用户访问网站时就会在浏览器端弹出基本认证窗口:
![http-auth-2.png](http-auth-2.png)
输入我们刚才在创建鉴权方式时添加的用户名和密码就可以登录访问我们的网站资源。
### 限定文件类型和域名
你可以在某个鉴权方式中--"更多选项"中来限定文件扩展名和域名:
![http-auth-3.png](http-auth-3.png)
在上图中的示例中,我们限定了只有文件扩展名`.png``.jpg``.jpeg``.mp3``.mp4`,并且域名为 `example.com` 才会启用鉴权;其他文件扩展名的或者其他域名的,都将会直接放行。
## 多个鉴权方式
如果添加了多个鉴权方式,那么只有第一个启用的鉴权方式是有效的。
## 关闭鉴权方式
你可以取消"启用鉴权"的勾选,并保存,即为取消访问鉴权。
## 鉴权失败
如果失败,将会返回 `401 Unauthorized`,并在访问日志中标记鉴权失败时的鉴权方式。

View File

@@ -0,0 +1,41 @@
# HTTP报头
## 设置
可以在网站"设置" -- "HTTP报头"中添加自定义报头。
其中:
* `响应报头` - 发送给客户端的报头
* `请求报头` - 发送给源站的报头
添加时,请严格遵守大小写规范,比如 `Content-Type` 的以下写法都是**错误**的:
~~~
content-type
content_type
contentType
~~~
严重注意在HTTP/2中所有报头在浏览器端查看时可能显示的是小写比如`content-type`但是在GoEdge系统添加的时候一定要严格地写成`Xxx-Yyy`类似的格式。
### 变量
可以在报头值中使用变量,具体可以使用的变量列表在[这里](variables.md)查看。
### 删除报头
如果想删除一个报头,也可以在这里添加"需要删除的报头",同样注意大小写规范。
## 示例
### 传递IP地址
有时候需要使用一个自定义的报头(比如叫`X-Example-Forwarded-For`向源站传递IP地址可以添加一个报头
~~~
X-Example-Forwarded-For: ${remoteAddr}
~~~
### 实现浏览器端跨域访问
在同个网页展示多个域名的资源时,比如`https://example.org`调用`https://example.com`,浏览器可能会出现类似于以下的错误提示:
~~~
Uncaught DOMException: Blocked a frame with origin "https://www.example.com" from accessing a cross-origin frame.
~~~
或者
~~~
Access to XMLHttpRequest at 'https://example.com/...' (redirected from 'https://example.org/...') from origin 'https://example.org' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
~~~
出现这些的错误提示后,当前网页可能无法正常加载。此时需要在被调用的域名(比如上面示例中的`https://example.com`)网站中增加[CORS](cors.md)相关报头。

View File

@@ -0,0 +1,14 @@
# 网站HTTP设置
一个服务需要开启HTTP设置后才能使用`http://`开头的网址访问。
在网站"设置" -- "HTTP"里:
![http1.png](http1.png)
* 启用HTTP选中后才会启用HTTP服务
* 自动跳转到HTTPS开启后所有HTTP的请求都会自动跳转到对应的HTTPS URL上默认所有的域名访问`http://`开头的网址时都会自动跳转到`https://`地址上,可以点击"更多选项"设置这个行为;
* 更多选项:
* 状态码跳转使用的HTTP状态码
* 跳转后域名或IP地址指定跳转后的HTTPS的域名或者IP地址默认和用户正在访问的域名或IP地址一致不填写就表示使用当前的域名
* 端口跳转后的端口号默认为443
* 允许的域名:允许跳转的域名,如果填写了允许的域名,那么只有这些域名可以自动跳转;
* 排除的域名:不允许跳转的域名,如果填写了排除的域名,那么这些域名将不跳转。

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 KiB

View File

@@ -0,0 +1,34 @@
# HTTP/3
## 启用网站HTTP/3功能
要想启用单个网站HTTP/3功能你需要
* 你已经启用了网站的HTTPS功能并上传了对应的SSL证书
* 在网站设置 -- HTTPS页面点击"启用HTTP/3"并保存如果没有这个选项说明你没有在对应的集群中启用HTTP/3功能。
![http3-2.png](http3-2.png)
## 测试
前提是你已经启用了浏览器的quic功能以下介绍可能根据浏览器语言设置不同而有不同的描述
* `Google Chrome` - 在浏览器上访问 `chrome://flags/#enable-quic`,找到"Experimental QUIC protocol"并启用之Enabled
* `Microsoft Edge` - 在浏览器上访问 `edge://flags/#enable-quic`,找到"Experimental QUIC protocol"并启用之Enabled
* `Firefox` - 在浏览器上访问 `about:config`,然后搜索"network.http.http3.enable"并点击对应行末尾的切换图标启用之true
* `Safari` - 最新版本的Safari已经自动支持HTTP/3无需任何额外配置
浏览器设置后,可能需要清理浏览器缓存并重启后才能生效。
在网站中启用HTTP/3功能后可以在浏览器正常访问你的网站域名并在浏览器控制台通常是F12快捷键观察访问使用的协议Protocol如果显示h3或者HTTP/3就说明你已经成功启用了网站的HTTP/3功能。
## 常见问题
### 为什么设置后仍然不能使用HTTP/3访问
可能的几个原因,请依次认真检查:
1. 你没有在网站中开启HTTP/3功能请参考本文中的 "启用单个网站HTTP/3功能" 部分进行检查;
2. 在浏览器控制台中检查响应Header中是否含有不区分大小写 `Alt-Svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000` 的内容如果没有则说明你没有在网站中开启HTTP/3功能
3. 检查你的浏览器是否设置了代理服务器或者你的电脑是否设置了代理服务器由于绝大部分代理服务器只支持TCP通讯所以如果你的电脑上有任何代理服务器请先停用
4. 尝试清空浏览器所有缓存后,关闭浏览器,再重新打开;
5. 另外注意Websocket目前不支持HTTP/3。
### 一定需要开启HTTP/2才能使用HTTP/3吗
不需要,两个是独立的不相互影响。
### 为什么关闭HTTP/3后访问的仍然是HTTP/3
浏览器端会记住你的HTTP/3设置所以在单个网站关闭HTTP/3功能后你访问到的仍然可能是HTTP/3此时你需要清空浏览器设置并重启浏览器后重试。

View File

@@ -0,0 +1,64 @@
# 网站HTTPS设置
可以使用HTTPS保护网站数据一个网站需要开启HTTPS设置后才能使用`https://`开头的网址访问。
在网站"设置" -- "HTTPS"里:
![https1](https1.png)
* 启用HTTPS选中后才会启用HTTPS服务
## SSL/TLS相关设置
* 启用HTTP/2选中后将支持HTTP/2
* 选择证书启用HTTPS后必须上传至少一个证书才能让HTTPS服务生效证书的域名需要和服务里绑定的域名一致
* TLS最低版本选择支持的TLS最低版本
* 更多选项:
* 加密算法套件:可以自定义支持的加密套件;
* 是否开启HSTS是否支持HSTS此功能可以让当前域名或者子域名自动从HTTP跳转到HTTPS详情可以看本文[HSTS](#HSTS)部分;
* 客户端认证方式:可以设置客户端是否需要证书;
* 客户端认证CA证书用来校验客户端证书以增强安全性通常不需要设置。
## HSTS
可以通过设置HSTS来强制浏览器使用安全的连接。可以在"SSL/TLS相关设置" -- "更多选项"中开启。开启后请求HTTPS时会自动发送一个响应Header
~~~
Strict-Transport-Security: max-age=31536000; includeSubDomains
~~~
之后所有使用HTTP协议的请求也会自动转到HTTPS直到HSTS过了有效期`max-age`)。
如果你在使用Google Chrome浏览器可以在浏览器上访问
~~~
chrome://net-internals/#hsts
~~~
来管理HSTS域名。
## OCSP Stapling
从v0.4.5开始可以设置HTTPS服务是否启用OCSP Stapling功能。
### 开启或关闭
可以在网站的"HTTPS"或者"TLS"设置中--"更多选项"--"OCSP Stapling"中开启或者关闭此功能。
### 使用OpenSSL检查链接的OCSP状态
可以使用 `openssl` 命令检查连接OCSP状态
~~~bash
openssl s_client -connect example.com:443 -servername example.com -status
~~~
可以把其中的两处 `example.com` 都换成你要检查的域名。
返回内容中如果包含了:
~~~
OCSP response:
======================================
OCSP Response Data:
OCSP Response Status: successful (0x0)
Response Type: Basic OCSP Response
Version: 1 (0x0)
...
~~~
就说明服务器成功返回了OCSP。
## 常见问题
### 怎么设置自动从HTTP跳转到HTTPS
有两种简单方法可以设置自动从HTTP跳转到HTTPS:
* ["HTTP"](http.md)设置 -- "自动跳转到HTTPS"
* ["URL跳转"](redirect.md) 设置里添加一个跳转规则。
本文前面提到HSTS也可以强制网站从HTTP跳转到HTTPS使用但通常不建议使用此功能因为会在用户浏览器上保存设置导致后期无法很方便地修改。

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 KiB

View File

@@ -0,0 +1,47 @@
# IPv6
IPv6是英文“Internet Protocol Version 6”互联网协议第6版的缩写可以视作IPv4的升级版。
一些IPv4的例子如下
~~~
192.168.1.100
220.181.108.113
~~~
都是我们最常见的形式。
一个IPv6的例子如下
~~~
2406:8c00:0:3409:133:18:203:158
~~~
可以看到各段之间用的是冒号(`:`)。
## GoEdge中的IPv6
在GoEdge中的IPv6多用于绑定的网络地址比如网络端口监听地址、源站地址等通常形式为
~~~
[IPv6地址]:端口
~~~
需要用`[]`来括住IP地址。
比如:
~~~
[2406:8c00:0:3409:133:18:203:158]:8080
~~~
但是如果不是在地址中使用IPv6就不需要用括号来括住。
目前IPv6地址可以应用在以下几个地方
* 源站地址
## 零值网络
在IPv4中我们通常使用`0.0.0.0`表示不限制网络接口的地址,也就是说比如我们绑定了`0.0.0.0`地址那么通过所有网卡绑定的IP地址都能访问。
在IPv6中我们使用
~~~
[::]:端口
[::]:8081
~~~
来表达同样的意思。而且在GoEdge中`0.0.0.0``[::]`是可以通用的,两者的效果一样,对于同一个端口来说,同时只能使用一种版本。
## 防火墙设置
如果你启用了`ip6tables`,记得在`ip6tables`中添加对应端口的ACCEPT规则。

View File

@@ -0,0 +1,34 @@
# PROXY Protocol
CDN节点和源站之间可以通过PROXY Protocol功能传递访客的真实IP。
## 设置
在单个网站设置的”源站“ -- “更多设置”页面中 -- “更多选项”中可以选中“PROXY Protocol”一栏中的复选框
![proxy-protocol1.png](proxy-protocol1.png)
然后保存即可。
其中一般情况下不用担心选择哪个版本通常来说现代的Web服务都能自动识别。
设置完成后你需要同时设置源站Web服务不然源站无法正常访问。
## 源站Web服务设置
启用PROXY Protocol后你需要在你的源站服务器上的Web服务上也启用PROXY Protocol功能以nginx为例可以使用以下配置
~~~nginx
http {
...
# 在访问日志中需要增加 $proxy_protocol_addr 变量来获取真正的访客地址
log_format main '$proxy_protocol_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent";
...
server {
# 在对应监听端口需要增加 proxy_protocol 协议支持
listen 8080 proxy_protocol;
...
}
}
~~~

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 KiB

View File

@@ -0,0 +1,80 @@
# 网站常见问题
## 502 Bad Gateway 是怎么回事?
出现 `502 Bad Gateway` 通常说明源站无法连接,可以在访问日志里查看详情(如果开启了访问日志的话)。此时,请检查:
1. 你有没有在源站中设置源站;
2. 你是否在源站设置中设置了源站的专属域名,导致用户访问专属域名以外的域名时无法匹配到源站;
3. 你填写的源站的协议、域名、端口等信息是否正确;
4. 检查源站是否返回了`502`
5. 通过 `curl` 或者 `wget` 等工具在边缘节点上直接访问源站URL检查在边缘节点上是否能正常访问源站
6. 如果源站设置有防火墙或者限流设置,请关闭或者设置边缘节点为白名单。
## 504 Gateway Timeout 是怎么回事?
出现 `504 Gateway Timeout` 通常说明源站连接超时,可以在访问日志里查看详情(如果开启了访问日志的话)。此时,请检查:
1. 检查源站的协议、域名、端口是否正确;
2. 源站是否返回了`504`
3. 通过 `curl` 或者 `wget` 等工具在边缘节点上直接访问源站URL检查在边缘节点上是否能正常访问源站
4. 如果源站设置有防火墙或者限流设置,请关闭或者设置边缘节点为白名单。
在访问日志详情(访问日志后面图标点开)中"综合信息"页面会给出具体的错误,通常是:
* `dial tcp: xxx.xxx.xxx.xxx .. i/o timeout` - 指的是节点通过TCP连接源站超时通常是节点和源站的网络连接状况不佳也有可能是源站设置了一些限流、限速措施导致边缘节点无法正常连接源站
* `dial tcp: lookup example.com on xxx.xxx.xxx.xxx` - 指的是节点通过TCP连接域名解析服务超时请检查节点设置的域名解析服务服务器地址是否正确、是否可以正常连接`Linux` 上通常可以查看 `/etc/resolv.conf`
* `dial udp: lookup example.com on xxx.xxx.xxx.xxx` - 指的是节点通过UDP连接域名解析服务超时请检查节点设置的域名解析服务服务器地址是否正确、是否可以正常连接`Linux` 上通常可以查看 `/etc/resolv.conf`
* `tls handshake timeout` - TLS握手超时
## 使用CDN后报头无法读取
有些自定义的报头经过CDN之后源站无法读取可能的原因是GoEdge会自动将用户自定义的报头名称换为标准的报头比如用户的请求中含有
~~~
auth-key: MTIzNDU2
X-WWW-Echo: Helo
~~~
那么经过CDN之后源站接收到的报头会变成
~~~
Auth-Key: MTIzNDU2
X-Www-Echo: Helo
~~~
在HTTP/2中在浏览器端查看的报头可能全部是小写的但是实际在传输中报头都是首字母大写的。
知道原因后,我们就有了解决方案:
* 方法1修改源站的程序从原先不标准的报头名称改为标准的报头名称比如从 `auth-key` 改为 `Auth-Key`
* 方法2在网站设置"HTTP报头" -- "请求报头"中自定义一个新的非标准报头,比如名称为`auth-key`,值为`${header.Auth-Key}`,这样可以强行添加一个非标准报头在请求中,源站接收到的就是非标准的`auth-key`
## 使用CDN后浏览器无限重定向
如果使用CDN后浏览器端显示`Too Many Redirects`等无限重定向的现象,那么常见的可能原因有如下几个:
1. 可能原因1源站中设置的源站域名和CDN域名一致如果源站的域名和用户访问CDN的域名是一样的那么CDN将无法正常读取源站请务必检查源站设置防止源站中域名配置和CDN冲突
2. 可能原因2源站地址是HTTP但是在源站设置了自动跳转到HTTPS所以导致源站一直在HTTP-HTTPS之间不断跳转这种情况下有以下几个解决方法
1. 取消源站的自动跳转设置(或者根据当前访问域名来判断是否跳转,而不是全部强制跳转);
2. 源站地址改为HTTPS的防止源站自动跳转。
## 出现 ERR_SSL_PROTOCOL_ERROR 错误是怎么回事?
如果你在浏览器上打开网站出现类似于以下的错误:
~~~
此网站无法提供安全连接
xxx.com 发送的响应无效。
ERR_SSL_PROTOCOL_ERROR
~~~
通常原因:
1. 可能开启了网站443端口但是没有上传SSL证书导致你可以上传证书或者关闭`443`端口来解决此问题;
2. 你没有在对应的网站里绑定当前访问的域名;
3. 可能错误地在HTTP里将端口设置为443请改为在HTTPS里设置这个端口。
## 开启HTTPS后浏览器提示 ERR_SSL_VERSION_OR_CIPHER_MISMATCH
通常的几个原因:
* 没有上传对应域名的SSL证书导致请上传证书后再试
* 绑定的域名没有填写正确,请在"域名"设置里查看域名是否填写正确、是否有同一个域名绑定多个服务的情形。
## 已经上传了证书但是浏览器提示不安全
如果已经在管理系统绑定了域名,且已经上传此域名对应的证书,在浏览器上访问时仍然提示"不安全"类似字样,可能的几个原因:
1. 你访问的是HTTP网址而不是HTTPS网址比如 `http://你的域名`此时请换成HTTPS访问直接输入 `https://你的域名` 来验证你的HTTPS设置是否成功如果使用HTTPS访问成功你只需要在网站设置里设置让HTTP自动跳转到HTTPS即可
2. 你访问的域名没有在"域名"设置里加入;
3. 你访问的域名证书不正确。
## 源站提示无法找到域名
如果源站提示 `Invalid Hostname` 之类的错误,或者提示找不到对应的网站,或者显示的是别的域名对应网站的内容,原因是你没有设置正确的回源主机名。默认情况下,回源主机名和终端用户访问的域名一致,但如果你的源站不支持用户直接访问的域名的话,可以在"源站" -- "更多设置"中统一修改回源主机名,修改为源站域名;如果你每个源站域名不一样的话,可以点开修改每个源站,然后设置对应的回源主机名;如果源站使用的是特殊的端口,那么你设置的回源主机名中可能也需要添加对应的端口(比如 `example.com:9200`)。
## 499是什么错误
客户端在发送请求过程中取消请求的时候会记录一个499通常可以忽略。
## 如何禁止用户通过节点IP访问网站
可以在对应集群设置 -- "网站设置"中开启"禁止未绑定域名访问",并取消"允许使用节点IP访问"选项勾选,然后保存。

View File

@@ -0,0 +1,51 @@
# URL跳转
URL跳转主要允许我们设置不同域名之间的URL跳转。
## 设置跳转
可以在网站"详情" -- "URL跳转"中点击"创建"按钮创建一个规则:
![redirect1.png](redirect1.png)
如上图所示:
* 跳转前URL 跳转之前的URL需要填写完整的URL包括`http`或者`https`,如果非`80``443`的端口,也需要填入端口;
* 匹配模式:
* 匹配前缀只要访问的URL头部部分包含跳转前URL即可跳转
* 精准匹配精准匹配跳转前的URL即只有访问完全一样的URL才会跳转即用户访问的URL和我们填入的"跳转前URL"内容一模一样时才会跳转;
* 正则匹配可以在跳转前URL中使用正则表达式然后可以在跳转后URL中使用正则表达式中括号的变量比如`${1}``${2}`等;
* 是否保留URL路径参数选中后则跳转之后保留跳转之前的URL路径和参数比如跳转前的URL是`http://goedge.cn/docs?v=1`那么跳转后的URL也会带上`/docs?v=1`而变成`https://goedge.cn/docs?v=1`
* 跳转后URL系统匹配到跳转前URL之后要跳转到的URL
* 跳转状态码跳转时使用的HTTP状态码通常选择默认就行默认的HTTP状态码是`307`
* 匹配条件如果对跳转前URL匹配还有别的约束条件可以在这里添加
* 是否启用:使用启用当前规则。
各个项目填写完成后,点击底部的"保存"按钮即可保存设置。
注意:如果你设置的跳转规则没有起作用,请清空浏览器缓存或者换个浏览器尝试,确认是不是缓存的问题。
## 示例
### 所有端口跳转到单一端口
比如将所有端口号包括80/443链接都跳转到某个固定端口比如 `8888`
* 跳转前URL^(?U)(http|https)://(.+)(:\d+)?(/.*)$
* 匹配模式:正则匹配
* 跳转后URL(这里假设你的8888是httpshttps://${2}:8888${4}
不用担心访问 `8888` 时会无限跳转系统会自动判断跳转前后URL是否一致如果一致将不会重复跳转。
### 域名跳转
比如跳转前:
~~~
https://example.com/hello
https://example1.com/hello
https://example2.com/hello
~~~
跳转后:
~~~
https://goedge.cn/hello
~~~
除了可以将"跳转类型"设置为域名跳转来实现外,还可以使用正则表达式:
* 跳转前URL`^https://(.+?)/(.*)$`,表达式中的 `^` 符号表示开始,`$` 符号表示结束
* 匹配模式:正则匹配
* 跳转后URLhttps://goedge.cn/${2}
## HTTP到HTTPS
如果你的应用场景是希望将HTTP都自动跳转到HTTPS可以在[HTTP设置](http.md)里快速设置。

Binary file not shown.

After

Width:  |  Height:  |  Size: 310 KiB

View File

@@ -0,0 +1,22 @@
# 防盗链
GoEdge提供了简单的防盗链功能可以根据请求来源`Referer`)来决定是否拦截。
## 启用防盗链
在网站设置中 -- "防盗链" 中可以启用防盗链功能:
![referers1.png](referers1.png)
其中:
* `允许直接访问网站` - 允许用户直接访问网站用户第一次访问网站时来源域名通常为空注意在某些浏览器上比如Chrome从HTTPS网站跳转到HTTP网站时可能不会携带来源域名信息
* `来源域名允许一致` - 允许来源域名和当前访问的域名一致,相当于在站内访问
* `允许的来源域名` - 允许的其他来源域名列表
* `禁止的来源域名` - 禁止的来源域名列表;如果一个来源域名既在允许的来源域名中,又在禁止的来源域名中,那么最终将会被禁止
被防盗链阻止后页面将会提示403类似于
~~~
403 Forbidden
The referer has been blocked.
Request ID: 1673496051116481000001.
~~~
如果是中文编码优先的浏览器,将会以中文进行提示。

Binary file not shown.

After

Width:  |  Height:  |  Size: 285 KiB

View File

@@ -0,0 +1,65 @@
# 访客IP地址
可以在网站"设置"--"访客IP地址"中设置访客的IP地址获取方式。
## 影响范围
访客IP地址设置会应用于记录访问日志、检查IP黑白名单、WAF地区封禁、IP并发限制等处。
## 选项说明
### 直接获取
用户直接访问边缘节点,即 "用户 --> 边缘节点" 模式这时候可以直接从连接中读取到真实的IP地址。
### 从上级代理中获取
用户和边缘节点之间有别的代理服务转发,即 "用户 --> `[第三方代理服务]` --> 边缘节点"这时候只能从上级代理中获取传递的IP地址。
用户首先访问的第三方代理服务比如Nginx或者Squid之类的反向代理服务再通过这些服务转发到GoEdge的边缘节点边缘节点上可以自动从 `X-Real-IP``X-Forwarded-For` 两个报头中获取用户的真实IP。
### 从请求报头中读取
从GoEdge v1.2.9开始可以填写单个或多个请求报头用于从中读取真实的客户端IP
* 单个请求报头示例:`X-Real-IP``X-Client-IP``X-My-IP`
* 多个请求报头示例(使用英文逗号分隔):`X-Real-IP,X-Client-IP`
### 自定义变量
可以自定义变量,在 [这里](variables.md) 可以查看支持的请求变量。
## 错误处理
如果从选项或者变量值获得的IP地址格式是错误的非IPv4和IPv6格式则自动使用直接连接边缘节点的连接IP地址。
## 第三方代理示例
### 从Nginx传递客户端IP地址到边缘节点
如果Nginx处在GoEdge的下游就是"用户" --> Nginx --> GoEdge可以在Nginx中使用 `X-Real-IP` 报头将获取的IP地址传递给GoEdge:
~~~nginx
server {
...
location / {
proxy_pass http://127.0.0.1:8005; # proxy_pass $GOEDGE节点地址
proxy_set_header X-Real-IP $remote_addr; # 设置 X-Real-IP
proxy_http_version 1.1; # 设置HTTP/1.1
proxy_set_header Connection 'keep-alive'; # 设置保持连接
}
}
~~~
## 源站读取IP
默认地,边缘节点会传递 `X-Real-IP``X-Forwarded-For` 两个报头到源站,其中`X-Real-IP`只可能为单个IP`X-Forwarded-For`中可能包含有多个IP
~~~
# X-Forwarded-For语法
X-Forwarded-For: <client>, <proxy1>, <proxy2>
# 示例
X-Forwarded-For: 192.168.2.100, 192.168.2.1, 127.0.0.1
~~~
从上面示例中可以看出,要获取`X-Forwarded-For`中的IP值需要分隔多个IP并读取第一个。
### PHP读取客户端IP示例
~~~php
$_SERVER["HTTP_X_FORWARDED_FOR"]
$_SERVER["HTTP_X_REAL_IP"]
~~~
### Go读取客户端IP示例
~~~go
req.Header.Get("X-Forwarded-For")
req.Header.Get("X-Real-Ip") // 注意这里的Ip格式
~~~

View File

@@ -0,0 +1,15 @@
# 重写规则
可以在网站“设置” -- “重写规则”中添加重写规则将某个或者某一类URL映射到另外一个或一组URL上。
## 示例
### 示例1将 `/a/abc.html` 重写到 `/b/abc.html`
* 匹配规则:`^/a/(.+)$`
* 目标URL`/b/${1}` (其中`${1}`是匹配规则中第一个括号匹配的内容)
### 示例2将任何URL都增加一个 `/prefix` 目录
* 匹配规则:`^(.+)$`
* 目标URL`/prefix${1}` (这里没有加入`/`,因为匹配的内容中已经包含)
### 示例3将 `/123_456.html` 类似的URL重写为目录
* 匹配规则:`^/(\d+)_(\d+)\.html$` (其中`\d`表示数字,并注意`.`符号是特殊符号,需要转义变成`\.`
* 目标URL`/${1}/${2}/index.html` (变成`/123/456/index.html`

View File

@@ -0,0 +1,22 @@
# UserAgent名单
UserAgent名单简称UA名单可以根据浏览器或其他客户端发送的`User-Agent`信息决定是否拦截用户请求。
## 启用UA名单
在网站设置中 -- "UA名单"中可以启用UA名单
![user-agent1.png](user-agent1.png)
点击加号(`+`可以添加UA关键词
![user-agent2.png](user-agent2.png)
其中:
* `UA关键词` - 可以是`Chrome`,也可以使用星号(`*`)通配符,比如`*Firefox*`也可以不填任何内容直接确定表示空的UserAgent关键词不区分大小写即`Chrome`和`chrome`是一样的;
* `动作` - 可以选"允许"或"不允许"如果是不允许用此UserAgent访问时就会被阻止当既有允许的UA关键词也有不允许的UA关键词时优先判断"允许"的UA关键词一旦允许后即不再往下执行
被UA名单阻止后页面将会提示403类似于
~~~
403 Forbidden
The User-Agent has been blocked.
Request ID: 1673496724695481000001.
~~~
如果是中文编码优先的浏览器,将会以中文进行提示。

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 249 KiB

View File

@@ -0,0 +1,159 @@
# 内置变量
## 变量形式
在使用变量的地方,使用`${varName}`来表示变量,比如:
~~~
/${1}/hello${requestPath}?name=${arg.name}
~~~
## 内置变量列表
### 请求相关变量
* `${edgeVersion}` - 边缘节点的版本
* `${remoteAddr}` - 客户端地址IP会依次根据X-Forwarded-For、X-Real-IP、RemoteAddr获取适合前端有别的反向代理服务时使用如果前端没有别的反向代理则存在被伪造的风险
* `${rawRemoteAddr}` - 客户端地址IP返回直接连接服务的客户端原始IP地址如果前端有别的反向代理可能会返回前端反向代理的地址而不是真实的客户端地址
* `${remotePort}` - 客户端端口
* `${remoteUser}` - 客户端用户名
* `${requestId}` - 请求ID类似于`1638414954148000001`v0.3.7加入
* `${requestURI}` - 请求URI比如`/hello?name=lily`
* `${requestPath}` - 请求路径(不包括参数),比如`/hello`
* `${requestURL}` - 完整的请求URL比如`https://example.com/hello?name=lily`v0.2.5版本加入
* `${requestPathExtension}` - 请求路径中的文件扩展名,包括点符号,比如`.html``.png`
* `${requestPathLowerExtension}` 请求路径中的文件扩展名,其中大写字母会被自动转换为小写,包括点符号,比如`.html``.png`在v0.6.4中加入
* `${requestLength}` - 请求内容长度
* `${requestMethod}` - 请求方法,比如`GET``POST`
* `${requestFilename}` - 请求文件路径
* `${scheme}` - 请求协议,`http``https`
* `${proto}` - 包含版本的HTTP请求协议类似于`HTTP/1.0`
* `${timeISO8601}` - ISO 8601格式的时间比如`2018-07-16T23:52:24.839+08:00`
* `${timeLocal}` - 本地时间,比如`17/Jul/2018:09:52:24 +0800`
* `${msec}` - 带有毫秒的时间,比如`1531756823.054`
* `${timestamp}` - unix时间戳单位为秒
* `${host}` - 主机名,通常是请求的域名
* `${cname}` - 当前网站的CNAME比如38b48e4f.example.com在v0.5.8中加入
* `${host.first}` - 主机名第一段,比如`www.example.com`的值为`www`
* `${host.last}` - 主机名最后一段,比如`www.example.com`的值为`com`
* `${host.N}` - 主机名的第几段从0开始N最大为4`${host.0}``${host.1}`...`${host.4}`
* `${host.-N}` - 主机名的倒数第几段,从-1开始比如对于`www.example.com``${host.-1}`值为`com`N最大为5
* `${serverName}` - 接收请求的服务器名
* `${serverAddr}` - 服务器地址IP需要管理员设置启用后才会生效
* `${serverPort}` - 接收请求的服务器端口
* `${referer}` - 请求来源URL
* `${referer.host}` - 请求来源URL域名在v0.2.6中加入
* `${userAgent}` - 客户端信息
* `${contentType}` - 请求头部的Content-Type
* `${request}` - 构造类似于"GET / HTTP/1.1"之类的请求字符串
* `${cookies}` - 所有cookie组合字符串
* `${cookie.NAME}` - 单个cookie值比如`${cookie.sid}`
* `${isArgs}` - 问号(?标记如果URL有参数则值为`?`否则则值为空在v0.3.7中加入
* `${args}` - 所有URL参数组合字符串
* `${arg.NAME}` - 单个URL参数值比如`${arg.name}`
* `${headers}` - 所有Header信息组合字符串
* `${header.NAME}` - 单个请求Header值比如`${header.User-Agent}`
* `${documentRoot}` - 当前请求的文档根目录
#### 地区相关
* `${geo.country.name}` - 国家/地区名称v0.4.0加入
* `${geo.country.id}` - 国家/地区IDv0.4.0加入
* `${geo.province.name}` - 省份名称目前只包含中国省份v0.4.0加入
* `${geo.province.id}` - 省份ID目前只包含中国城市v0.4.0加入
* `${geo.city.name}` - 城市名称目前只包含中国城市v0.4.0加入
* `${geo.city.id}` - 城市ID目前只包含中国城市v0.4.0加入
* `${geo.town.name}` - 县级单位名称目前只包含中国县级单位v0.4.0加入
* `${geo.town.id}` - 县级单位ID目前只包含中国县级单位v0.4.0加入
#### ISP相关
* `${isp.name}` - ISP服务商名称v0.4.0加入
* `${isp.id}` - ISP服务商IDv0.4.0加入
#### 浏览器相关
* `${browser.os.name}` - 客户端所在操作系统名称v0.4.0加入
* `${browser.os.version}` - 客户端所在操作系统版本v0.4.0加入
* `${browser.name}` - 客户端浏览器名称v0.4.0加入
* `${browser.version}` - 客户端浏览器版本v0.4.0加入
* `${browser.isMobile}` - 手机标识如果客户端是手机则值为1否则为0v0.4.0加入
#### 产品相关
* `${product.name}` - 产品名,可以在"系统设置"--"管理界面设置"中设置v0.4.0加入
* `${product.version}` - 产品版本,可以在"系统设置"--"管理界面设置"中设置v0.4.0加入
### 响应相关变量
* `${requestTime}` - 请求花费时间
* `${bytesSent}` - 发送的内容长度包括Header字节
* `${bodyBytesSent}` - 发送的内容长度不包括Header字节
* `${status}` - 状态码,比如`200`
* `${statusMessage}` - 状态消息,比如`200 OK`
* `${response.contentType}` - 响应的`Content-Type`
* `${response.header.NAME}` - 响应中的Header值
### 源站相关变量
> 注意在缓存Key中不能使用源站相关变量因为读取缓存是在读取源站之前执行的。
* `${origin.id}` - 源站服务器ID
* `${origin.code}` - 源站服务器代号
* `${origin.address}` - 源站服务器地址,包含主机地址和端口
* `${origin.host}` - 源站服务器地址,只包含主机地址
* `${origin.scheme}` - 源站服务器协议
### 缓存相关变量
* `${cache.status}` - 缓存状态,值可能为:
* `BYPASS` - 没有开启缓存策略或者其他原因未通过缓存策略处理的时候,状态为`BYPASS`
* `HIT` - 已命中缓存
* `MISS` - 未命中缓存
* `PURGE` - 正在清除缓存
* `UPDATING` - 正在更新缓存v0.3.7加入
* `EXPIRED` - 已过期
* `STALE` - 正在使用过期的缓存
* `${cache.age}` - 缓存对象的有效期v0.3.7加入
* `${cache.policy.id}` - 缓存策略ID
* `${cache.policy.name}` - 缓存策略名称
* `${cache.policy.type}` - 缓存策略类型memory、file之类
* `${cache.key}` - 当前请求的缓存Keyv0.3.7加入
## 修饰符
从GoEdge v1.3.3开始,变量中可以加入修饰符,用于对变量值再次进行处理:
~~~
${varName|修饰符1|修饰符2|更多修饰符...}
~~~
目前支持以下几个修饰符:
* `urlEncode` - 对变量值进行URL编码
* `urlDecode` - 对变量值进行URL解码
* `base64Encode` - 对变量值进行Base64编码
* `base64Decode` - 对变量值进行Base64解码
* `md5` - 对变量值进行md5编码
* `sha1` - 对变量值进行sha1编码
* `sha256` - 对变量值进行sha256编码
* `toLowerCase` - 转换为小写
* `toUpperCase` - 转换为大写
示例:
~~~
${requestPath|urlEncode} // "/hello/world" => "%2Fhello%2Fworld"
${arg.name|base64Encode|md5} // "Lily" => "0374cb368f88b84dfb57bb883cca02ec"
~~~
## 匹配变量
匹配变量值的是有正则表达式的地方使用匹配结果通常为一个从0开始的数字比如在重写规则中
~~~
/(\w+)/(\w+)
~~~
那么
* `${0}` - 表示整体匹配的内容
* `${1}` - 表示第一个括号匹配的内容
* `${2}` - 表示第二个括号匹配的内容
可以使用`(?i)`来设置不区分大小写:
~~~
(?i)/index.php
~~~
更多可用的正则表达式可以参考 [RE2 Syntax](https://github.com/google/re2/wiki/Syntax)。
### 命名变量
也可以给变量设置一个名称:
~~~
/(?P<myName>\w+)
~~~
然后就可以在待替换字符串中使用 `${myName}`
~~~
/hello/${myName}
~~~

View File

@@ -0,0 +1,34 @@
# 国家/地区封禁
可以在WAF中设置只允许一组区域或者禁止一组区域访问我们的网站。
在网站设置 -- "WAF" -- "国家/地区封禁"中可以添加仅允许的区域和仅封禁的区域:
![](deny-countries.png)
其中:
* `仅允许的区域` - 只有所添加的区域才被允许访问我们的网站;
* `仅封禁的区域` - 只有所添加的区域才被禁止访问我们的网站,其他没有添加的区域都允许访问我们网站;
* 更多选项
* `例外URL` - 需要排除的URL在这里配置的URL将不会触发封禁
* `限制URL` - 只有在限制URL列表里的URL才会触发封禁
* `提示内容` - 可以设置在用户被封禁时网页上提示的文字支持完整的HTML`<html>...</html>`
上面的`仅允许的区域``仅封禁的区域`同时只会有一个有效,以`仅允许的区域`作为优先。
## 示例
### 只允许大中华区访问
大中华区包括中国内地、港澳台,所以可以在"仅允许的区域"中点击"修改",出现以下界面:
![](deny-countries2.png)
其中,我们选中"中国",就已经包含了全部大中华区,然后点"确定"
![](deny-countries3.png)
然后点击底部的"保存"按钮即可。
### 封禁缅甸访问
缅甸是我们随意选取的一个区域,仅供测试使用,可以在"仅封禁的区域"中点击"修改",出现以下界面:
![](deny-countries4.png)
其中,我们选中"缅甸",然后点"确定"
![](deny-countries5.png)
然后点击底部的"保存"按钮即可。

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 KiB

View File

@@ -0,0 +1,27 @@
# 省份封禁
可以在WAF中设置只允许一组中国省份或者禁止一组省份直辖市等访问我们的网站。
在网站设置 -- "WAF" -- "省份封禁"中可以添加仅允许的省份和仅封禁的省份:
![](deny-provinces.png)
其中:
* `仅允许的省份` - 只有所添加的省份才被允许访问我们的网站;
* `仅封禁的省份` - 只有所添加的省份才被禁止访问我们的网站,其他没有添加的省份都允许访问我们网站;
* 更多选项
* `例外URL` - 需要排除的URL在这里配置的URL将不会触发封禁
* `限制URL` - 只有在限制URL列表里的URL才会触发封禁
* `提示内容` - 可以设置在用户被封禁时网页上提示的文字支持完整的HTML`<html>...</html>`
上面的`仅允许的省份``仅封禁的省份`同时只会有一个有效,以`仅允许的省份`作为优先。
## 示例
### 只允许北京访问
可以在"仅允许的省份"中点击"修改",出现以下界面:
![](deny-provinces2.png)
然后点"确定"
![](deny-provinces3.png)
然后点击底部的"保存"按钮即可。

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 KiB

View File

@@ -0,0 +1,28 @@
# WebP
在v0.3.2以后可以设置自动将图片转换为WebP从而大幅减少带宽的使用。
## 设置
可以在网站"设置" -- "WebP"中设置:
![webp1](webp1.png)
其中:
* `启用WebP压缩` - 是否启用WebP转换注意从v0.4.7开始,只有满足缓存条件的图片内容才会被转换,防止实时转换耗费大量的系统资源;
* `支持的扩展名` - 需要转换的URL扩展名
* `支持的MimeType` - 需要转换的Content-Type类型只要一个响应的文件满足扩展名或者MimeType其中之一条件就可以被转换
* 更多选项:
* `内容最小长度` - 需要转换的图片内容最小长度;默认没有限制;
* `内容最大长度` - 需要转换的图片内容最大长度;默认没有限制;
* `匹配条件` - 额外的匹配条件。
同样的在单个路由规则中也可以设置WebP自动转换。
设置后,可以在浏览器端查看图片文件的`Content-Type`,如果是 `image/webp` 即为转换成功:
~~~
...
Content-Type: image/webp
...
~~~
WebP转换并不会修改文件的文件名。
## 缓存
由于WebP需要将图片内容全部读取后再转换消耗了更多的系统资源所以在开启WebP自动转换后请同时在缓存设置里开启对图片的缓存比如缓存`.jpg``.jpeg``.png``.ico`等扩展名的URL。

Binary file not shown.

After

Width:  |  Height:  |  Size: 293 KiB

View File

@@ -0,0 +1,11 @@
# Websocket
可以在网站"设置" -- "Websocket"里设置是否开启Websocket。
![websocket1.png](websocket1.png)
上图中:
* 启用Websocket启用后网站才会提供Websocket功能
* 允许所有来源域:是否允许所有的来源域(`Origin`如果选中了表示允许在所有的域名上访问此Websocket
* 如果没有选中允许所有来源域,则可以设置允许的域名:
* 允许的来源域列表只允许在此列表中的来源域名访问Websocket服务
* 更多选项:
* 是否传递请求来源域选中表示把接收到的请求Header中的`Origin`字段传递到源站。

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 KiB

View File

@@ -0,0 +1 @@
请点击左侧菜单中的帮助内容。

View File

@@ -0,0 +1,773 @@
List of CodeMirror contributors. Updated before every release.
4oo4
4r2r
Aaron Brooks
Abdelouahab
Abdussalam Abdurrahman
Abe Fettig
Abhishek Gahlot
Adam Ahmed
Adam King
Adam Particka
adanlobato
Adán Lobato
Adrian Aichner
Adrian Heine
Adrien Bertrand
aeroson
Ahmad Amireh
Ahmad M. Zawawi
ahoward
Akeksandr Motsjonov
Alasdair Smith
AlbertHilb
Alberto González Palomo
Alberto Pose
Albert Xing
Alexander Pavlov
Alexander Schepanovski
Alexander Shvets
Alexander Solovyov
Alexandre Bique
alexey-k
Alex Piggott
Aliaksei Chapyzhenka
Allen Sarkisyan
Ami Fischman
Amin Shali
Amin Ullah Khan
amshali@google.com
Amsul
amuntean
Amy
Ananya Sen
anaran
AndersMad
Anders Nawroth
Anderson Mesquita
Anders Wåglund
Andrea G
Andreas Reischuck
Andres Taylor
Andre von Houck
Andrew Cheng
Andrew Dassonville
Andrey Fedorov
Andrey Klyuchnikov
Andrey Lushnikov
Andrey Shchekin
Andy Joslin
Andy Kimball
Andy Li
Angelo
angelozerr
angelo.zerr@gmail.com
Ankit
Ankit Ahuja
Ansel Santosa
Anthony Dugois
anthonygego
Anthony Gégo
Anthony Grimes
Anton Kovalyov
Apollo Zhu
AQNOUCH Mohammed
Aram Shatakhtsyan
areos
Arnab Bose
Arsène von Wyss
Arthur Müller
Arun Narasani
as3boyan
asolove
atelierbram
AtomicPages LLC
Atul Bhouraskar
Aurelian Oancea
Axel Lewenhaupt
Baptiste Augrain
Barret Rennie
Bartosz Dziewoński
Basarat Ali Syed
Bastian Müller
belhaj
Bem Jones-Bey
benbro
Beni Cherniavsky-Paskin
Benjamin DeCoste
Benjamin Young
Ben Keen
Ben Miller
Ben Mosher
Bernhard Sirlinger
Bert Chang
Bharad
BigBlueHat
Billy Moon
binny
Bjorn Hansen
B Krishna Chaitanya
Blaine G
blukat29
Bo
boomyjee
Bo Peng
borawjm
Brad Metcalf
Brandon Frohs
Brandon Wamboldt
Bret Little
Brett Zamir
Brian Grinstead
Brian Sletten
brrd
Bruce Mitchener
Bryan Massoth
Caitlin Potter
Calin Barbat
callodacity
Camilo Roca
Casey Klebba
César González Íñiguez
Chad Jolly
Chandra Sekhar Pydi
Charles Skelton
Cheah Chu Yeow
Chris Coyier
Chris Ford
Chris Granger
Chris Houseknecht
Chris Lohfink
Chris Morgan
Chris Reeves
Chris Smith
Christian Gruen
Christian Oyarzun
Christian Petrov
christopherblaser
Christopher Brown
Christopher Kramer
Christopher Mitchell
Christopher Pfohl
Chunliang Lyu
ciaranj
CodeAnimal
CodeBitt
coderaiser
Cole R Lawrence
ComFreek
Cristian Prieto
Curran Kelleher
Curtis Gagliardi
dagsta
daines
Dale Jung
Dan Bentley
Dan Heberden
Daniel, Dao Quang Minh
Daniele Di Sarli
Daniel Faust
Daniel Hanggi
Daniel Huigens
Daniel Kesler
Daniel KJ
Daniel Neel
Daniel Parnell
Daniel Thwaites
Danila Malyutin
Danny Yoo
darealshinji
Darius Roberts
databricks-david-lewis
Dave Brondsema
Dave MacLachlan
Dave Myers
David Barnett
David H. Bronke
David Mignot
David Pathakjee
David Santana
David Vázquez
David Whittington
deebugger
Deep Thought
Devin Abbott
Devon Carew
Dick Choi
dignifiedquire
Dimage Sapelkin
dmaclach
Dmitry Kiselyov
domagoj412
Dominator008
Domizio Demichelis
Doug Blank
Doug Wikle
Drew Bratcher
Drew Hintz
Drew Khoury
Drini Cami
Dror BG
duralog
dwelle
eborden
edsharp
ekhaled
Elisée
Emmanuel Schanzer
Enam Mijbah Noor
Eric Allam
Eric Bogard
Erik Welander
eustas
Fabien Dubosson
Fabien O'Carroll
Fabio Zendhi Nagao
Faiza Alsaied
Fauntleroy
fbuchinger
feizhang365
Felipe Lalanne
Felix Raab
ficristo
Filip Noetzel
Filip Stollár
Filype Pereira
finalfantasia
flack
Florian Felten
ForbesLindesay
Forbes Lindesay
Ford_Lawnmower
Forrest Oliphant
Franco Catena
Frank Wiegand
Fredrik Borg
Gabriel Gheorghian
Gabriel Horner
Gabriel Nahmias
galambalazs
Gary Sheng
Gautam Mehta
Gavin Douglas
gekkoe
Geordie Hall
George Stephanis
geowarin
Gerard Braad
Gergely Hegykozi
Giovanni Calò
Glebov Boris
Glenn Jorde
Glenn Ruehle
goldsmcb
Golevka
Google LLC
Gordon Smith
Grant Skinner
greengiant
Gregory Koberger
Grzegorz Mazur
Guan Gui
Guillaume Massé
Guillaume Massé
guraga
Gustavo Rodrigues
Hakan Tunc
Hans Engel
Harald Schilly
Hardest
Harshvardhan Gupta
Hasan Karahan
Heanes
Hector Oswaldo Caballero
Hélio
Hendrik Wallbaum
Henrik Haugbølle
Herculano Campos
hidaiy
Hiroyuki Makino
hitsthings
Hocdoc
Hugues Malphettes
Ian Beck
Ian Davies
Ian Dickinson
Ian Rose
Ian Wehrman
Ian Wetherbee
Ice White
ICHIKAWA, Yuji
idleberg
ilvalle
Ingo Richter
Irakli Gozalishvili
Ivan Kurnosov
Ivoah
Jacob Lee
Jaimin
Jake Peyser
Jakob Miland
Jakub Vrana
Jakub Vrána
James Campos
James Howard
James Thorne
Jamie Hill
Jamie Morris
Jan Jongboom
jankeromnes
Jan Keromnes
Jan Odvarko
Jan Schär
Jan T. Sott
Jared Dean
Jared Forsyth
Jared Jacobs
Jason
Jason Barnabe
Jason Grout
Jason Johnston
Jason San Jose
Jason Siefken
Jayaprabhakar
Jaydeep Solanki
Jean Boussier
Jeff Blaisdell
Jeff Hanke
Jeff Jenkins
jeffkenton
Jeff Pickhardt
jem (graphite)
Jeremy Parmenter
Jim
Jim Avery
jkaplon
JobJob
jochenberger
Jochen Berger
Joel Einbinder
joelpinheiro
Johan Ask
John Connor
John-David Dalton
John Engler
John Lees-Miller
John Snelson
John Van Der Loo
Jon Ander Peñalba
Jonas Döbertin
Jonas Helfer
Jonathan Hart
Jonathan Malmaud
Jon Gacnik
jongalloway
Jon Malmaud
Jon Sangster
Joost-Wim Boekesteijn
Joseph Pecoraro
Josh Barnes
Josh Cohen
Josh Soref
Joshua Newman
Josh Watzman
jots
jsoojeon
ju1ius
Juan Benavides Romero
Jucovschi Constantin
Juho Vuori
Julien CROUZET
Julien Rebetez
Justin Andresen
Justin Hileman
jwallers@gmail.com
kaniga
karevn
Karol
Kayur Patel
Kazuhito Hokamura
Ken Newman
ken restivo
Ken Rockot
Kevin Earls
Kevin Kwok
Kevin Muret
Kevin Sawicki
Kevin Ushey
Kier Darby
Klaus Silveira
Koh Zi Han, Cliff
komakino
Konstantin Lopuhin
koops
Kris Ciccarello
ks-ifware
kubelsmieci
KwanEsq
Kyle Kelley
KyleMcNutt
Lanfei
Lanny
laobubu
Laszlo Vidacs
leaf corcoran
Lemmon
Leonid Khachaturov
Leon Sorokin
Leonya Khachaturov
Liam Newman
Libo Cannici
Lior Goldberg
Lior Shub
LloydMilligan
LM
lochel
Lorenzo Simionato
Lorenzo Stoakes
Louis Mauchet
Luca Fabbri
Luciano Longo
Lu Fangjian
Luke Browning
Luke Granger-Brown
Luke Stagner
lynschinzer
M1cha
Madhura Jayaratne
Maksim Lin
Maksym Taran
Malay Majithia
Manideep
Manuel Rego Casasnovas
Marat Dreizin
Marcel Gerber
Marcelo Camargo
Marco Aurélio
Marco Munizaga
Marcus Bointon
Marek Rudnicki
Marijn Haverbeke
Mário Gonçalves
Mario Pietsch
Mark Anderson
Mark Lentczner
Marko Bonaci
Mark Peace
Markus Bordihn
Markus Olsson
Martin Balek
Martín Gaitán
Martin Hasoň
Martin Hunt
Martin Laine
Martin Zagora
Mason Malone
Mateusz Paprocki
Mathias Bynens
mats cronqvist
Matt Gaide
Matthew Bauer
Matthew Beale
matthewhayes
Matthew Rathbone
Matthew Suozzo
Matthias Bussonnier
Matthias BUSSONNIER
Matt MacPherson
Matt McDonald
Matt Pass
Matt Sacks
mauricio
Maximilian Hils
Maxim Kraev
Max Kirsch
Max Schaefer
Max Xiantu
mbarkhau
McBrainy
mce2
melpon
meshuamam
Metatheos
Micah Dubinko
Michael
Michael Goderbauer
Michael Grey
Michael Kaminsky
Michael Lehenbauer
Michael Walker
Michael Zhou
Michal Čihař
Michal Dorner
Michal Kapiczynski
Mighty Guava
Miguel Castillo
mihailik
Mika Andrianarijaona
Mike
Mike Bostock
Mike Brevoort
Mike Diaz
Mike Ivanov
Mike Kadin
Mike Kobit
Milan Szekely
MinRK
Miraculix87
misfo
mkaminsky11
mloginov
Moritz Schubotz (physikerwelt)
Moritz Schwörer
Moshe Wajnberg
mps
ms
mtaran-google
Mu-An Chiou
Mu-An ✌️ Chiou
mzabuawala
Narciso Jaramillo
Nathan Williams
ndr
Neil Anderson
neon-dev
nerbert
NetworkNode
nextrevision
ngn
nguillaumin
Ng Zhi An
Nicholas Bollweg
Nicholas Bollweg (Nick)
NickKolok
Nick Kreeger
Nick Small
Nicolas Kick
Nicolò Ribaudo
Niels van Groningen
nightwing
Nikita Beloglazov
Nikita Vasilyev
Nikolaj Kappler
Nikolay Kostov
nilp0inter
Nisarg Jhaveri
nlwillia
noragrossman
Norman Rzepka
opl-
Oreoluwa Onatemowo
Oskar Segersvärd
overdodactyl
pablo
pabloferz
Pablo Zubieta
paddya
Page
paladox
Panupong Pasupat
paris
Paris
Paris Kasidiaris
Patil Arpith
Patrick Stoica
Patrick Strawderman
Paul Garvin
Paul Ivanov
Paul Masson
Pavel
Pavel Feldman
Pavel Petržela
Pavel Strashkin
Paweł Bartkiewicz
peteguhl
peter
Peter Flynn
peterkroon
Peter Kroon
Philipp A
Philipp Markovics
Philip Stadermann
Pi Delport
Pierre Gerold
Pieter Ouwerkerk
Pontus Melke
prasanthj
Prasanth J
Prayag Verma
Prendota
Qiang Li
Radek Piórkowski
Rahul
Rahul Anand
ramwin1
Randall Mason
Randy Burden
Randy Edmunds
Randy Luecke
Raphael Amorim
Rasmus Erik Voel Jensen
Rasmus Schultz
Raymond Hill
ray ratchup
Ray Ratchup
Remi Nyborg
Renaud Durlin
Reynold Xin
Richard Denton
Richard van der Meer
Richard Z.H. Wang
Rishi Goomar
Robert Brignull
Robert Crossfield
Roberto Abdelkader Martínez Pérez
robertop23
Robert Plummer
Rrandom
Rrrandom
Ruslan Osmanov
Ryan Petrello
Ryan Prior
ryu-sato
sabaca
Sam Lee
Sam Rawlins
Samuel Ainsworth
Sam Wilson
sandeepshetty
Sander AKA Redsandro
Sander Verweij
santec
Sarah McAlear and Wenlin Zhang
Sascha Peilicke
satamas
satchmorun
sathyamoorthi
Saul Costa
S. Chris Colbert
SCLINIC\jdecker
Scott Aikin
Scott Goodhew
Sebastian Wilzbach
Sebastian Zaha
Seren D
Sergey Goder
Sergey Tselovalnikov
Se-Won Kim
Shane Liesegang
shaund
shaun gilchrist
Shawn A
Shea Bunge
sheopory
Shil S
Shiv Deepak
Shmuel Englard
Shubham Jain
Siamak Mokhtari
silverwind
Simon Edwards
sinkuu
snasa
soliton4
sonson
Sorab Bisht
spastorelli
srajanpaliwal
Stanislav Oaserele
stan-z
Stas Kobzar
Stefan Borsje
Steffen Beyer
Steffen Bruchmann
Steffen Kowalski
Stephane Moore
Stephen Lavelle
Steve Champagne
Steve Hoover
Steve O'Hara
stoskov
Stu Kennedy
Sungho Kim
sverweij
Taha Jahangir
takamori
Tako Schotanus
Takuji Shimokawa
Takuya Matsuyama
Tarmil
TDaglis
tel
Tentone
tfjgeorge
Thaddee Tyl
thanasis
TheHowl
themrmax
think
Thomas Brouard
Thomas Dvornik
Thomas Kluyver
Thomas Schmid
Tim Alby
Tim Baumann
Timothy Farrell
Timothy Gu
Timothy Hatcher
Tobias Bertelsen
TobiasBg
Todd Berman
Todd Kennedy
Tomas-A
Tomas Varaneckas
Tom Erik Støwer
Tom Klancer
Tom MacWright
Tom McLaughlin
Tony Jian
tophf
totalamd
Travis Heppe
Triangle717
Tristan Tarrant
TSUYUSATO Kitsune
Tugrul Elmas
twifkak
Tyler Long
Vadzim Ramanenka
Vaibhav Sagar
VapidWorx
Vestimir Markov
vf
Victor Bocharsky
Vincent Woo
Volker Mische
vtripolitakis
Weiyan Shao
wenli
Wes Cossick
Wesley Wiser
Weston Ruter
Will Binns-Smith
Will Dean
William Jamieson
William Stein
Willy
Wojtek Ptak
wonderboyjon
Wu Cheng-Han
Xavier Mendez
Yassin N. Hassan
YNH Webdev
Yunchi Luo
Yuvi Panda
Yvonnick Esnault
Zac Anger
Zachary Dremann
Zeno Rocha
Zhang Hao
Ziv
zziuni
魏鹏刚

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,92 @@
# How to contribute
- [Getting help](#getting-help)
- [Submitting bug reports](#submitting-bug-reports)
- [Contributing code](#contributing-code)
## Getting help
Community discussion, questions, and informal bug reporting is done on the
[discuss.CodeMirror forum](http://discuss.codemirror.net).
## Submitting bug reports
The preferred way to report bugs is to use the
[GitHub issue tracker](http://github.com/codemirror/CodeMirror/issues). Before
reporting a bug, read these pointers.
**Note:** The issue tracker is for *bugs*, not requests for help. Questions
should be asked on the
[discuss.CodeMirror forum](http://discuss.codemirror.net) instead.
### Reporting bugs effectively
- CodeMirror is maintained by volunteers. They don't owe you anything, so be
polite. Reports with an indignant or belligerent tone tend to be moved to the
bottom of the pile.
- Include information about **the browser in which the problem occurred**. Even
if you tested several browsers, and the problem occurred in all of them,
mention this fact in the bug report. Also include browser version numbers and
the operating system that you're on.
- Mention which release of CodeMirror you're using. Preferably, try also with
the current development snapshot, to ensure the problem has not already been
fixed.
- Mention very precisely what went wrong. "X is broken" is not a good bug
report. What did you expect to happen? What happened instead? Describe the
exact steps a maintainer has to take to make the problem occur. We can not
fix something that we can not observe.
- If the problem can not be reproduced in any of the demos included in the
CodeMirror distribution, please provide an HTML document that demonstrates
the problem. The best way to do this is to go to
[jsbin.com](http://jsbin.com/ihunin/edit), enter it there, press save, and
include the resulting link in your bug report.
## Contributing code
Note that we are not accepting any new addons or modes into the main
distribution. If you've written such a module, please distribute it as
a separate NPM package.
- Make sure you have a [GitHub Account](https://github.com/signup/free)
- Fork [CodeMirror](https://github.com/codemirror/CodeMirror/)
([how to fork a repo](https://help.github.com/articles/fork-a-repo))
- Make your changes
- If your changes are easy to test or likely to regress, add tests.
Tests for the core go into `test/test.js`, some modes have their own
test suite under `mode/XXX/test.js`. Feel free to add new test
suites to modes that don't have one yet (be sure to link the new
tests into `test/index.html`).
- Follow the general code style of the rest of the project (see
below). Run `bin/lint` to verify that the linter is happy.
- Make sure all tests pass. Visit `test/index.html` in your browser to
run them.
- Submit a pull request
([how to create a pull request](https://help.github.com/articles/fork-a-repo)).
Don't put more than one feature/fix in a single pull request.
By contributing code to CodeMirror you
- agree to license the contributed code under CodeMirror's [MIT
license](https://codemirror.net/LICENSE).
- confirm that you have the right to contribute and license the code
in question. (Either you hold all rights on the code, or the rights
holder has explicitly granted the right to use it like this,
through a compatible open source license or through a direct
agreement with you.)
### Coding standards
- 2 spaces per indentation level, no tabs.
- Note that the linter (`bin/lint`) which is run after each commit
complains about unused variables and functions. Prefix their names
with an underscore to muffle it.
- CodeMirror does *not* follow JSHint or JSLint prescribed style.
Patches that try to 'fix' code to pass one of these linters will be
unceremoniously discarded.

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (C) 2017 by Marijn Haverbeke <marijnh@gmail.com> and others
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,49 @@
# CodeMirror
[![Build Status](https://travis-ci.org/codemirror/CodeMirror.svg)](https://travis-ci.org/codemirror/CodeMirror)
[![NPM version](https://img.shields.io/npm/v/codemirror.svg)](https://www.npmjs.org/package/codemirror)
[![Join the chat at https://gitter.im/codemirror/CodeMirror](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/codemirror/CodeMirror)
[Funding status: ![maintainer happiness](https://marijnhaverbeke.nl/fund/status_s.png?again)](https://marijnhaverbeke.nl/fund/)
CodeMirror is a versatile text editor implemented in JavaScript for
the browser. It is specialized for editing code, and comes with over
100 language modes and various addons that implement more advanced
editing functionality. Every language comes with fully-featured code
and syntax highlighting to help with reading and editing complex code.
A rich programming API and a CSS theming system are available for
customizing CodeMirror to fit your application, and extending it with
new functionality.
You can find more information (and the
[manual](https://codemirror.net/doc/manual.html)) on the [project
page](https://codemirror.net). For questions and discussion, use the
[discussion forum](https://discuss.codemirror.net/).
See
[CONTRIBUTING.md](https://github.com/codemirror/CodeMirror/blob/master/CONTRIBUTING.md)
for contributing guidelines.
The CodeMirror community aims to be welcoming to everybody. We use the
[Contributor Covenant
(1.1)](http://contributor-covenant.org/version/1/1/0/) as our code of
conduct.
### Installation
Either get the [zip file](https://codemirror.net/codemirror.zip) with
the latest version, or make sure you have [Node](https://nodejs.org/)
installed and run:
npm install codemirror
**NOTE**: This is the source repository for the library, and not the
distribution channel. Cloning it is not the recommended way to install
the library, and will in fact not work unless you also run the build
step.
### Quickstart
To build the project, make sure you have Node.js installed (at least version 6)
and then `npm install`. To run, just open `index.html` in your
browser (you don't need to run a webserver). Run the tests with `npm test`.

View File

@@ -0,0 +1,209 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
var noOptions = {};
var nonWS = /[^\s\u00a0]/;
var Pos = CodeMirror.Pos;
function firstNonWS(str) {
var found = str.search(nonWS);
return found == -1 ? 0 : found;
}
CodeMirror.commands.toggleComment = function(cm) {
cm.toggleComment();
};
CodeMirror.defineExtension("toggleComment", function(options) {
if (!options) options = noOptions;
var cm = this;
var minLine = Infinity, ranges = this.listSelections(), mode = null;
for (var i = ranges.length - 1; i >= 0; i--) {
var from = ranges[i].from(), to = ranges[i].to();
if (from.line >= minLine) continue;
if (to.line >= minLine) to = Pos(minLine, 0);
minLine = from.line;
if (mode == null) {
if (cm.uncomment(from, to, options)) mode = "un";
else { cm.lineComment(from, to, options); mode = "line"; }
} else if (mode == "un") {
cm.uncomment(from, to, options);
} else {
cm.lineComment(from, to, options);
}
}
});
// Rough heuristic to try and detect lines that are part of multi-line string
function probablyInsideString(cm, pos, line) {
return /\bstring\b/.test(cm.getTokenTypeAt(Pos(pos.line, 0))) && !/^[\'\"\`]/.test(line)
}
function getMode(cm, pos) {
var mode = cm.getMode()
return mode.useInnerComments === false || !mode.innerMode ? mode : cm.getModeAt(pos)
}
CodeMirror.defineExtension("lineComment", function(from, to, options) {
if (!options) options = noOptions;
var self = this, mode = getMode(self, from);
var firstLine = self.getLine(from.line);
if (firstLine == null || probablyInsideString(self, from, firstLine)) return;
var commentString = options.lineComment || mode.lineComment;
if (!commentString) {
if (options.blockCommentStart || mode.blockCommentStart) {
options.fullLines = true;
self.blockComment(from, to, options);
}
return;
}
var end = Math.min(to.ch != 0 || to.line == from.line ? to.line + 1 : to.line, self.lastLine() + 1);
var pad = options.padding == null ? " " : options.padding;
var blankLines = options.commentBlankLines || from.line == to.line;
self.operation(function() {
if (options.indent) {
var baseString = null;
for (var i = from.line; i < end; ++i) {
var line = self.getLine(i);
var whitespace = line.slice(0, firstNonWS(line));
if (baseString == null || baseString.length > whitespace.length) {
baseString = whitespace;
}
}
for (var i = from.line; i < end; ++i) {
var line = self.getLine(i), cut = baseString.length;
if (!blankLines && !nonWS.test(line)) continue;
if (line.slice(0, cut) != baseString) cut = firstNonWS(line);
self.replaceRange(baseString + commentString + pad, Pos(i, 0), Pos(i, cut));
}
} else {
for (var i = from.line; i < end; ++i) {
if (blankLines || nonWS.test(self.getLine(i)))
self.replaceRange(commentString + pad, Pos(i, 0));
}
}
});
});
CodeMirror.defineExtension("blockComment", function(from, to, options) {
if (!options) options = noOptions;
var self = this, mode = getMode(self, from);
var startString = options.blockCommentStart || mode.blockCommentStart;
var endString = options.blockCommentEnd || mode.blockCommentEnd;
if (!startString || !endString) {
if ((options.lineComment || mode.lineComment) && options.fullLines != false)
self.lineComment(from, to, options);
return;
}
if (/\bcomment\b/.test(self.getTokenTypeAt(Pos(from.line, 0)))) return
var end = Math.min(to.line, self.lastLine());
if (end != from.line && to.ch == 0 && nonWS.test(self.getLine(end))) --end;
var pad = options.padding == null ? " " : options.padding;
if (from.line > end) return;
self.operation(function() {
if (options.fullLines != false) {
var lastLineHasText = nonWS.test(self.getLine(end));
self.replaceRange(pad + endString, Pos(end));
self.replaceRange(startString + pad, Pos(from.line, 0));
var lead = options.blockCommentLead || mode.blockCommentLead;
if (lead != null) for (var i = from.line + 1; i <= end; ++i)
if (i != end || lastLineHasText)
self.replaceRange(lead + pad, Pos(i, 0));
} else {
self.replaceRange(endString, to);
self.replaceRange(startString, from);
}
});
});
CodeMirror.defineExtension("uncomment", function(from, to, options) {
if (!options) options = noOptions;
var self = this, mode = getMode(self, from);
var end = Math.min(to.ch != 0 || to.line == from.line ? to.line : to.line - 1, self.lastLine()), start = Math.min(from.line, end);
// Try finding line comments
var lineString = options.lineComment || mode.lineComment, lines = [];
var pad = options.padding == null ? " " : options.padding, didSomething;
lineComment: {
if (!lineString) break lineComment;
for (var i = start; i <= end; ++i) {
var line = self.getLine(i);
var found = line.indexOf(lineString);
if (found > -1 && !/comment/.test(self.getTokenTypeAt(Pos(i, found + 1)))) found = -1;
if (found == -1 && nonWS.test(line)) break lineComment;
if (found > -1 && nonWS.test(line.slice(0, found))) break lineComment;
lines.push(line);
}
self.operation(function() {
for (var i = start; i <= end; ++i) {
var line = lines[i - start];
var pos = line.indexOf(lineString), endPos = pos + lineString.length;
if (pos < 0) continue;
if (line.slice(endPos, endPos + pad.length) == pad) endPos += pad.length;
didSomething = true;
self.replaceRange("", Pos(i, pos), Pos(i, endPos));
}
});
if (didSomething) return true;
}
// Try block comments
var startString = options.blockCommentStart || mode.blockCommentStart;
var endString = options.blockCommentEnd || mode.blockCommentEnd;
if (!startString || !endString) return false;
var lead = options.blockCommentLead || mode.blockCommentLead;
var startLine = self.getLine(start), open = startLine.indexOf(startString)
if (open == -1) return false
var endLine = end == start ? startLine : self.getLine(end)
var close = endLine.indexOf(endString, end == start ? open + startString.length : 0);
var insideStart = Pos(start, open + 1), insideEnd = Pos(end, close + 1)
if (close == -1 ||
!/comment/.test(self.getTokenTypeAt(insideStart)) ||
!/comment/.test(self.getTokenTypeAt(insideEnd)) ||
self.getRange(insideStart, insideEnd, "\n").indexOf(endString) > -1)
return false;
// Avoid killing block comments completely outside the selection.
// Positions of the last startString before the start of the selection, and the first endString after it.
var lastStart = startLine.lastIndexOf(startString, from.ch);
var firstEnd = lastStart == -1 ? -1 : startLine.slice(0, from.ch).indexOf(endString, lastStart + startString.length);
if (lastStart != -1 && firstEnd != -1 && firstEnd + endString.length != from.ch) return false;
// Positions of the first endString after the end of the selection, and the last startString before it.
firstEnd = endLine.indexOf(endString, to.ch);
var almostLastStart = endLine.slice(to.ch).lastIndexOf(startString, firstEnd - to.ch);
lastStart = (firstEnd == -1 || almostLastStart == -1) ? -1 : to.ch + almostLastStart;
if (firstEnd != -1 && lastStart != -1 && lastStart != to.ch) return false;
self.operation(function() {
self.replaceRange("", Pos(end, close - (pad && endLine.slice(close - pad.length, close) == pad ? pad.length : 0)),
Pos(end, close + endString.length));
var openEnd = open + startString.length;
if (pad && startLine.slice(openEnd, openEnd + pad.length) == pad) openEnd += pad.length;
self.replaceRange("", Pos(start, open), Pos(start, openEnd));
if (lead) for (var i = start + 1; i <= end; ++i) {
var line = self.getLine(i), found = line.indexOf(lead);
if (found == -1 || nonWS.test(line.slice(0, found))) continue;
var foundEnd = found + lead.length;
if (pad && line.slice(foundEnd, foundEnd + pad.length) == pad) foundEnd += pad.length;
self.replaceRange("", Pos(i, found), Pos(i, foundEnd));
}
});
return true;
});
});

View File

@@ -0,0 +1,78 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
function continueComment(cm) {
if (cm.getOption("disableInput")) return CodeMirror.Pass;
var ranges = cm.listSelections(), mode, inserts = [];
for (var i = 0; i < ranges.length; i++) {
var pos = ranges[i].head
if (!/\bcomment\b/.test(cm.getTokenTypeAt(pos))) return CodeMirror.Pass;
var modeHere = cm.getModeAt(pos)
if (!mode) mode = modeHere;
else if (mode != modeHere) return CodeMirror.Pass;
var insert = null;
if (mode.blockCommentStart && mode.blockCommentContinue) {
var line = cm.getLine(pos.line).slice(0, pos.ch)
var end = line.lastIndexOf(mode.blockCommentEnd), found
if (end != -1 && end == pos.ch - mode.blockCommentEnd.length) {
// Comment ended, don't continue it
} else if ((found = line.lastIndexOf(mode.blockCommentStart)) > -1 && found > end) {
insert = line.slice(0, found)
if (/\S/.test(insert)) {
insert = ""
for (var j = 0; j < found; ++j) insert += " "
}
} else if ((found = line.indexOf(mode.blockCommentContinue)) > -1 && !/\S/.test(line.slice(0, found))) {
insert = line.slice(0, found)
}
if (insert != null) insert += mode.blockCommentContinue
}
if (insert == null && mode.lineComment && continueLineCommentEnabled(cm)) {
var line = cm.getLine(pos.line), found = line.indexOf(mode.lineComment);
if (found > -1) {
insert = line.slice(0, found);
if (/\S/.test(insert)) insert = null;
else insert += mode.lineComment + line.slice(found + mode.lineComment.length).match(/^\s*/)[0];
}
}
if (insert == null) return CodeMirror.Pass;
inserts[i] = "\n" + insert;
}
cm.operation(function() {
for (var i = ranges.length - 1; i >= 0; i--)
cm.replaceRange(inserts[i], ranges[i].from(), ranges[i].to(), "+insert");
});
}
function continueLineCommentEnabled(cm) {
var opt = cm.getOption("continueComments");
if (opt && typeof opt == "object")
return opt.continueLineComment !== false;
return true;
}
CodeMirror.defineOption("continueComments", null, function(cm, val, prev) {
if (prev && prev != CodeMirror.Init)
cm.removeKeyMap("continueComment");
if (val) {
var key = "Enter";
if (typeof val == "string")
key = val;
else if (typeof val == "object" && val.key)
key = val.key;
var map = {name: "continueComment"};
map[key] = continueComment;
cm.addKeyMap(map);
}
});
});

View File

@@ -0,0 +1,32 @@
.CodeMirror-dialog {
position: absolute;
left: 0; right: 0;
background: inherit;
z-index: 15;
padding: .1em .8em;
overflow: hidden;
color: inherit;
}
.CodeMirror-dialog-top {
border-bottom: 1px solid #eee;
top: 0;
}
.CodeMirror-dialog-bottom {
border-top: 1px solid #eee;
bottom: 0;
}
.CodeMirror-dialog input {
border: none;
outline: none;
background: transparent;
width: 20em;
color: inherit;
font-family: monospace;
}
.CodeMirror-dialog button {
font-size: 70%;
}

View File

@@ -0,0 +1,161 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
// Open simple dialogs on top of an editor. Relies on dialog.css.
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
function dialogDiv(cm, template, bottom) {
var wrap = cm.getWrapperElement();
var dialog;
dialog = wrap.appendChild(document.createElement("div"));
if (bottom)
dialog.className = "CodeMirror-dialog CodeMirror-dialog-bottom";
else
dialog.className = "CodeMirror-dialog CodeMirror-dialog-top";
if (typeof template == "string") {
dialog.innerHTML = template;
} else { // Assuming it's a detached DOM element.
dialog.appendChild(template);
}
CodeMirror.addClass(wrap, 'dialog-opened');
return dialog;
}
function closeNotification(cm, newVal) {
if (cm.state.currentNotificationClose)
cm.state.currentNotificationClose();
cm.state.currentNotificationClose = newVal;
}
CodeMirror.defineExtension("openDialog", function(template, callback, options) {
if (!options) options = {};
closeNotification(this, null);
var dialog = dialogDiv(this, template, options.bottom);
var closed = false, me = this;
function close(newVal) {
if (typeof newVal == 'string') {
inp.value = newVal;
} else {
if (closed) return;
closed = true;
CodeMirror.rmClass(dialog.parentNode, 'dialog-opened');
dialog.parentNode.removeChild(dialog);
me.focus();
if (options.onClose) options.onClose(dialog);
}
}
var inp = dialog.getElementsByTagName("input")[0], button;
if (inp) {
inp.focus();
if (options.value) {
inp.value = options.value;
if (options.selectValueOnOpen !== false) {
inp.select();
}
}
if (options.onInput)
CodeMirror.on(inp, "input", function(e) { options.onInput(e, inp.value, close);});
if (options.onKeyUp)
CodeMirror.on(inp, "keyup", function(e) {options.onKeyUp(e, inp.value, close);});
CodeMirror.on(inp, "keydown", function(e) {
if (options && options.onKeyDown && options.onKeyDown(e, inp.value, close)) { return; }
if (e.keyCode == 27 || (options.closeOnEnter !== false && e.keyCode == 13)) {
inp.blur();
CodeMirror.e_stop(e);
close();
}
if (e.keyCode == 13) callback(inp.value, e);
});
if (options.closeOnBlur !== false) CodeMirror.on(inp, "blur", close);
} else if (button = dialog.getElementsByTagName("button")[0]) {
CodeMirror.on(button, "click", function() {
close();
me.focus();
});
if (options.closeOnBlur !== false) CodeMirror.on(button, "blur", close);
button.focus();
}
return close;
});
CodeMirror.defineExtension("openConfirm", function(template, callbacks, options) {
closeNotification(this, null);
var dialog = dialogDiv(this, template, options && options.bottom);
var buttons = dialog.getElementsByTagName("button");
var closed = false, me = this, blurring = 1;
function close() {
if (closed) return;
closed = true;
CodeMirror.rmClass(dialog.parentNode, 'dialog-opened');
dialog.parentNode.removeChild(dialog);
me.focus();
}
buttons[0].focus();
for (var i = 0; i < buttons.length; ++i) {
var b = buttons[i];
(function(callback) {
CodeMirror.on(b, "click", function(e) {
CodeMirror.e_preventDefault(e);
close();
if (callback) callback(me);
});
})(callbacks[i]);
CodeMirror.on(b, "blur", function() {
--blurring;
setTimeout(function() { if (blurring <= 0) close(); }, 200);
});
CodeMirror.on(b, "focus", function() { ++blurring; });
}
});
/*
* openNotification
* Opens a notification, that can be closed with an optional timer
* (default 5000ms timer) and always closes on click.
*
* If a notification is opened while another is opened, it will close the
* currently opened one and open the new one immediately.
*/
CodeMirror.defineExtension("openNotification", function(template, options) {
closeNotification(this, close);
var dialog = dialogDiv(this, template, options && options.bottom);
var closed = false, doneTimer;
var duration = options && typeof options.duration !== "undefined" ? options.duration : 5000;
function close() {
if (closed) return;
closed = true;
clearTimeout(doneTimer);
CodeMirror.rmClass(dialog.parentNode, 'dialog-opened');
dialog.parentNode.removeChild(dialog);
}
CodeMirror.on(dialog, 'click', function(e) {
CodeMirror.e_preventDefault(e);
close();
});
if (duration)
doneTimer = setTimeout(close, duration);
return close;
});
});

View File

@@ -0,0 +1,47 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"))
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod)
else // Plain browser env
mod(CodeMirror)
})(function(CodeMirror) {
"use strict"
CodeMirror.defineOption("autoRefresh", false, function(cm, val) {
if (cm.state.autoRefresh) {
stopListening(cm, cm.state.autoRefresh)
cm.state.autoRefresh = null
}
if (val && cm.display.wrapper.offsetHeight == 0)
startListening(cm, cm.state.autoRefresh = {delay: val.delay || 250})
})
function startListening(cm, state) {
function check() {
if (cm.display.wrapper.offsetHeight) {
stopListening(cm, state)
if (cm.display.lastWrapHeight != cm.display.wrapper.clientHeight)
cm.refresh()
} else {
state.timeout = setTimeout(check, state.delay)
}
}
state.timeout = setTimeout(check, state.delay)
state.hurry = function() {
clearTimeout(state.timeout)
state.timeout = setTimeout(check, 50)
}
CodeMirror.on(window, "mouseup", state.hurry)
CodeMirror.on(window, "keyup", state.hurry)
}
function stopListening(_cm, state) {
clearTimeout(state.timeout)
CodeMirror.off(window, "mouseup", state.hurry)
CodeMirror.off(window, "keyup", state.hurry)
}
});

View File

@@ -0,0 +1,6 @@
.CodeMirror-fullscreen {
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;
height: auto;
z-index: 9;
}

View File

@@ -0,0 +1,41 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
CodeMirror.defineOption("fullScreen", false, function(cm, val, old) {
if (old == CodeMirror.Init) old = false;
if (!old == !val) return;
if (val) setFullscreen(cm);
else setNormal(cm);
});
function setFullscreen(cm) {
var wrap = cm.getWrapperElement();
cm.state.fullScreenRestore = {scrollTop: window.pageYOffset, scrollLeft: window.pageXOffset,
width: wrap.style.width, height: wrap.style.height};
wrap.style.width = "";
wrap.style.height = "auto";
wrap.className += " CodeMirror-fullscreen";
document.documentElement.style.overflow = "hidden";
cm.refresh();
}
function setNormal(cm) {
var wrap = cm.getWrapperElement();
wrap.className = wrap.className.replace(/\s*CodeMirror-fullscreen\b/, "");
document.documentElement.style.overflow = "";
var info = cm.state.fullScreenRestore;
wrap.style.width = info.width; wrap.style.height = info.height;
window.scrollTo(info.scrollLeft, info.scrollTop);
cm.refresh();
}
});

View File

@@ -0,0 +1,127 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
CodeMirror.defineExtension("addPanel", function(node, options) {
options = options || {};
if (!this.state.panels) initPanels(this);
var info = this.state.panels;
var wrapper = info.wrapper;
var cmWrapper = this.getWrapperElement();
var replace = options.replace instanceof Panel && !options.replace.cleared;
if (options.after instanceof Panel && !options.after.cleared) {
wrapper.insertBefore(node, options.before.node.nextSibling);
} else if (options.before instanceof Panel && !options.before.cleared) {
wrapper.insertBefore(node, options.before.node);
} else if (replace) {
wrapper.insertBefore(node, options.replace.node);
info.panels++;
options.replace.clear();
} else if (options.position == "bottom") {
wrapper.appendChild(node);
} else if (options.position == "before-bottom") {
wrapper.insertBefore(node, cmWrapper.nextSibling);
} else if (options.position == "after-top") {
wrapper.insertBefore(node, cmWrapper);
} else {
wrapper.insertBefore(node, wrapper.firstChild);
}
var height = (options && options.height) || node.offsetHeight;
this._setSize(null, info.heightLeft -= height);
if (!replace) {
info.panels++;
}
if (options.stable && isAtTop(this, node))
this.scrollTo(null, this.getScrollInfo().top + height)
return new Panel(this, node, options, height);
});
function Panel(cm, node, options, height) {
this.cm = cm;
this.node = node;
this.options = options;
this.height = height;
this.cleared = false;
}
Panel.prototype.clear = function() {
if (this.cleared) return;
this.cleared = true;
var info = this.cm.state.panels;
this.cm._setSize(null, info.heightLeft += this.height);
if (this.options.stable && isAtTop(this.cm, this.node))
this.cm.scrollTo(null, this.cm.getScrollInfo().top - this.height)
info.wrapper.removeChild(this.node);
if (--info.panels == 0) removePanels(this.cm);
};
Panel.prototype.changed = function(height) {
var newHeight = height == null ? this.node.offsetHeight : height;
var info = this.cm.state.panels;
this.cm._setSize(null, info.heightLeft -= (newHeight - this.height));
this.height = newHeight;
};
function initPanels(cm) {
var wrap = cm.getWrapperElement();
var style = window.getComputedStyle ? window.getComputedStyle(wrap) : wrap.currentStyle;
var height = parseInt(style.height);
var info = cm.state.panels = {
setHeight: wrap.style.height,
heightLeft: height,
panels: 0,
wrapper: document.createElement("div")
};
wrap.parentNode.insertBefore(info.wrapper, wrap);
var hasFocus = cm.hasFocus();
info.wrapper.appendChild(wrap);
if (hasFocus) cm.focus();
cm._setSize = cm.setSize;
if (height != null) cm.setSize = function(width, newHeight) {
if (newHeight == null) return this._setSize(width, newHeight);
info.setHeight = newHeight;
if (typeof newHeight != "number") {
var px = /^(\d+\.?\d*)px$/.exec(newHeight);
if (px) {
newHeight = Number(px[1]);
} else {
info.wrapper.style.height = newHeight;
newHeight = info.wrapper.offsetHeight;
info.wrapper.style.height = "";
}
}
cm._setSize(width, info.heightLeft += (newHeight - height));
height = newHeight;
};
}
function removePanels(cm) {
var info = cm.state.panels;
cm.state.panels = null;
var wrap = cm.getWrapperElement();
info.wrapper.parentNode.replaceChild(wrap, info.wrapper);
wrap.style.height = info.setHeight;
cm.setSize = cm._setSize;
cm.setSize();
}
function isAtTop(cm, dom) {
for (var sibling = dom.nextSibling; sibling; sibling = sibling.nextSibling)
if (sibling == cm.getWrapperElement()) return true
return false
}
});

View File

@@ -0,0 +1,63 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
CodeMirror.defineOption("placeholder", "", function(cm, val, old) {
var prev = old && old != CodeMirror.Init;
if (val && !prev) {
cm.on("blur", onBlur);
cm.on("change", onChange);
cm.on("swapDoc", onChange);
onChange(cm);
} else if (!val && prev) {
cm.off("blur", onBlur);
cm.off("change", onChange);
cm.off("swapDoc", onChange);
clearPlaceholder(cm);
var wrapper = cm.getWrapperElement();
wrapper.className = wrapper.className.replace(" CodeMirror-empty", "");
}
if (val && !cm.hasFocus()) onBlur(cm);
});
function clearPlaceholder(cm) {
if (cm.state.placeholder) {
cm.state.placeholder.parentNode.removeChild(cm.state.placeholder);
cm.state.placeholder = null;
}
}
function setPlaceholder(cm) {
clearPlaceholder(cm);
var elt = cm.state.placeholder = document.createElement("pre");
elt.style.cssText = "height: 0; overflow: visible";
elt.style.direction = cm.getOption("direction");
elt.className = "CodeMirror-placeholder";
var placeHolder = cm.getOption("placeholder")
if (typeof placeHolder == "string") placeHolder = document.createTextNode(placeHolder)
elt.appendChild(placeHolder)
cm.display.lineSpace.insertBefore(elt, cm.display.lineSpace.firstChild);
}
function onBlur(cm) {
if (isEmpty(cm)) setPlaceholder(cm);
}
function onChange(cm) {
var wrapper = cm.getWrapperElement(), empty = isEmpty(cm);
wrapper.className = wrapper.className.replace(" CodeMirror-empty", "") + (empty ? " CodeMirror-empty" : "");
if (empty) setPlaceholder(cm);
else clearPlaceholder(cm);
}
function isEmpty(cm) {
return (cm.lineCount() === 1) && (cm.getLine(0) === "");
}
});

View File

@@ -0,0 +1,51 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
CodeMirror.defineOption("rulers", false, function(cm, val) {
if (cm.state.rulerDiv) {
cm.state.rulerDiv.parentElement.removeChild(cm.state.rulerDiv)
cm.state.rulerDiv = null
cm.off("refresh", drawRulers)
}
if (val && val.length) {
cm.state.rulerDiv = cm.display.lineSpace.parentElement.insertBefore(document.createElement("div"), cm.display.lineSpace)
cm.state.rulerDiv.className = "CodeMirror-rulers"
drawRulers(cm)
cm.on("refresh", drawRulers)
}
});
function drawRulers(cm) {
cm.state.rulerDiv.textContent = ""
var val = cm.getOption("rulers");
var cw = cm.defaultCharWidth();
var left = cm.charCoords(CodeMirror.Pos(cm.firstLine(), 0), "div").left;
cm.state.rulerDiv.style.minHeight = (cm.display.scroller.offsetHeight + 30) + "px";
for (var i = 0; i < val.length; i++) {
var elt = document.createElement("div");
elt.className = "CodeMirror-ruler";
var col, conf = val[i];
if (typeof conf == "number") {
col = conf;
} else {
col = conf.column;
if (conf.className) elt.className += " " + conf.className;
if (conf.color) elt.style.borderColor = conf.color;
if (conf.lineStyle) elt.style.borderLeftStyle = conf.lineStyle;
if (conf.width) elt.style.borderLeftWidth = conf.width;
}
elt.style.left = (left + col * cw) + "px";
cm.state.rulerDiv.appendChild(elt)
}
}
});

View File

@@ -0,0 +1,187 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
var defaults = {
pairs: "()[]{}''\"\"",
triples: "",
explode: "[]{}"
};
var Pos = CodeMirror.Pos;
CodeMirror.defineOption("autoCloseBrackets", false, function(cm, val, old) {
if (old && old != CodeMirror.Init) {
cm.removeKeyMap(keyMap);
cm.state.closeBrackets = null;
}
if (val) {
ensureBound(getOption(val, "pairs"))
cm.state.closeBrackets = val;
cm.addKeyMap(keyMap);
}
});
function getOption(conf, name) {
if (name == "pairs" && typeof conf == "string") return conf;
if (typeof conf == "object" && conf[name] != null) return conf[name];
return defaults[name];
}
var keyMap = {Backspace: handleBackspace, Enter: handleEnter};
function ensureBound(chars) {
for (var i = 0; i < chars.length; i++) {
var ch = chars.charAt(i), key = "'" + ch + "'"
if (!keyMap[key]) keyMap[key] = handler(ch)
}
}
ensureBound(defaults.pairs + "`")
function handler(ch) {
return function(cm) { return handleChar(cm, ch); };
}
function getConfig(cm) {
var deflt = cm.state.closeBrackets;
if (!deflt || deflt.override) return deflt;
var mode = cm.getModeAt(cm.getCursor());
return mode.closeBrackets || deflt;
}
function handleBackspace(cm) {
var conf = getConfig(cm);
if (!conf || cm.getOption("disableInput")) return CodeMirror.Pass;
var pairs = getOption(conf, "pairs");
var ranges = cm.listSelections();
for (var i = 0; i < ranges.length; i++) {
if (!ranges[i].empty()) return CodeMirror.Pass;
var around = charsAround(cm, ranges[i].head);
if (!around || pairs.indexOf(around) % 2 != 0) return CodeMirror.Pass;
}
for (var i = ranges.length - 1; i >= 0; i--) {
var cur = ranges[i].head;
cm.replaceRange("", Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1), "+delete");
}
}
function handleEnter(cm) {
var conf = getConfig(cm);
var explode = conf && getOption(conf, "explode");
if (!explode || cm.getOption("disableInput")) return CodeMirror.Pass;
var ranges = cm.listSelections();
for (var i = 0; i < ranges.length; i++) {
if (!ranges[i].empty()) return CodeMirror.Pass;
var around = charsAround(cm, ranges[i].head);
if (!around || explode.indexOf(around) % 2 != 0) return CodeMirror.Pass;
}
cm.operation(function() {
var linesep = cm.lineSeparator() || "\n";
cm.replaceSelection(linesep + linesep, null);
cm.execCommand("goCharLeft");
ranges = cm.listSelections();
for (var i = 0; i < ranges.length; i++) {
var line = ranges[i].head.line;
cm.indentLine(line, null, true);
cm.indentLine(line + 1, null, true);
}
});
}
function contractSelection(sel) {
var inverted = CodeMirror.cmpPos(sel.anchor, sel.head) > 0;
return {anchor: new Pos(sel.anchor.line, sel.anchor.ch + (inverted ? -1 : 1)),
head: new Pos(sel.head.line, sel.head.ch + (inverted ? 1 : -1))};
}
function handleChar(cm, ch) {
var conf = getConfig(cm);
if (!conf || cm.getOption("disableInput")) return CodeMirror.Pass;
var pairs = getOption(conf, "pairs");
var pos = pairs.indexOf(ch);
if (pos == -1) return CodeMirror.Pass;
var triples = getOption(conf, "triples");
var identical = pairs.charAt(pos + 1) == ch;
var ranges = cm.listSelections();
var opening = pos % 2 == 0;
var type;
for (var i = 0; i < ranges.length; i++) {
var range = ranges[i], cur = range.head, curType;
var next = cm.getRange(cur, Pos(cur.line, cur.ch + 1));
if (opening && !range.empty()) {
curType = "surround";
} else if ((identical || !opening) && next == ch) {
if (identical && stringStartsAfter(cm, cur))
curType = "both";
else if (triples.indexOf(ch) >= 0 && cm.getRange(cur, Pos(cur.line, cur.ch + 3)) == ch + ch + ch)
curType = "skipThree";
else
curType = "skip";
} else if (identical && cur.ch > 1 && triples.indexOf(ch) >= 0 &&
cm.getRange(Pos(cur.line, cur.ch - 2), cur) == ch + ch) {
if (cur.ch > 2 && /\bstring/.test(cm.getTokenTypeAt(Pos(cur.line, cur.ch - 2)))) return CodeMirror.Pass;
curType = "addFour";
} else if (identical) {
var prev = cur.ch == 0 ? " " : cm.getRange(Pos(cur.line, cur.ch - 1), cur)
if (!CodeMirror.isWordChar(next) && prev != ch && !CodeMirror.isWordChar(prev)) curType = "both";
else return CodeMirror.Pass;
} else if (opening) {
curType = "both";
} else {
return CodeMirror.Pass;
}
if (!type) type = curType;
else if (type != curType) return CodeMirror.Pass;
}
var left = pos % 2 ? pairs.charAt(pos - 1) : ch;
var right = pos % 2 ? ch : pairs.charAt(pos + 1);
cm.operation(function() {
if (type == "skip") {
cm.execCommand("goCharRight");
} else if (type == "skipThree") {
for (var i = 0; i < 3; i++)
cm.execCommand("goCharRight");
} else if (type == "surround") {
var sels = cm.getSelections();
for (var i = 0; i < sels.length; i++)
sels[i] = left + sels[i] + right;
cm.replaceSelections(sels, "around");
sels = cm.listSelections().slice();
for (var i = 0; i < sels.length; i++)
sels[i] = contractSelection(sels[i]);
cm.setSelections(sels);
} else if (type == "both") {
cm.replaceSelection(left + right, null);
cm.triggerElectric(left + right);
cm.execCommand("goCharLeft");
} else if (type == "addFour") {
cm.replaceSelection(left + left + left + left, "before");
cm.execCommand("goCharRight");
}
});
}
function charsAround(cm, pos) {
var str = cm.getRange(Pos(pos.line, pos.ch - 1),
Pos(pos.line, pos.ch + 1));
return str.length == 2 ? str : null;
}
function stringStartsAfter(cm, pos) {
var token = cm.getTokenAt(Pos(pos.line, pos.ch + 1))
return /\bstring/.test(token.type) && token.start == pos.ch &&
(pos.ch == 0 || !/\bstring/.test(cm.getTokenTypeAt(pos)))
}
});

View File

@@ -0,0 +1,175 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
/**
* Tag-closer extension for CodeMirror.
*
* This extension adds an "autoCloseTags" option that can be set to
* either true to get the default behavior, or an object to further
* configure its behavior.
*
* These are supported options:
*
* `whenClosing` (default true)
* Whether to autoclose when the '/' of a closing tag is typed.
* `whenOpening` (default true)
* Whether to autoclose the tag when the final '>' of an opening
* tag is typed.
* `dontCloseTags` (default is empty tags for HTML, none for XML)
* An array of tag names that should not be autoclosed.
* `indentTags` (default is block tags for HTML, none for XML)
* An array of tag names that should, when opened, cause a
* blank line to be added inside the tag, and the blank line and
* closing line to be indented.
*
* See demos/closetag.html for a usage example.
*/
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"), require("../fold/xml-fold"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror", "../fold/xml-fold"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
CodeMirror.defineOption("autoCloseTags", false, function(cm, val, old) {
if (old != CodeMirror.Init && old)
cm.removeKeyMap("autoCloseTags");
if (!val) return;
var map = {name: "autoCloseTags"};
if (typeof val != "object" || val.whenClosing)
map["'/'"] = function(cm) { return autoCloseSlash(cm); };
if (typeof val != "object" || val.whenOpening)
map["'>'"] = function(cm) { return autoCloseGT(cm); };
cm.addKeyMap(map);
});
var htmlDontClose = ["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param",
"source", "track", "wbr"];
var htmlIndent = ["applet", "blockquote", "body", "button", "div", "dl", "fieldset", "form", "frameset", "h1", "h2", "h3", "h4",
"h5", "h6", "head", "html", "iframe", "layer", "legend", "object", "ol", "p", "select", "table", "ul"];
function autoCloseGT(cm) {
if (cm.getOption("disableInput")) return CodeMirror.Pass;
var ranges = cm.listSelections(), replacements = [];
var opt = cm.getOption("autoCloseTags");
for (var i = 0; i < ranges.length; i++) {
if (!ranges[i].empty()) return CodeMirror.Pass;
var pos = ranges[i].head, tok = cm.getTokenAt(pos);
var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;
if (inner.mode.name != "xml" || !state.tagName) return CodeMirror.Pass;
var html = inner.mode.configuration == "html";
var dontCloseTags = (typeof opt == "object" && opt.dontCloseTags) || (html && htmlDontClose);
var indentTags = (typeof opt == "object" && opt.indentTags) || (html && htmlIndent);
var tagName = state.tagName;
if (tok.end > pos.ch) tagName = tagName.slice(0, tagName.length - tok.end + pos.ch);
var lowerTagName = tagName.toLowerCase();
// Don't process the '>' at the end of an end-tag or self-closing tag
if (!tagName ||
tok.type == "string" && (tok.end != pos.ch || !/[\"\']/.test(tok.string.charAt(tok.string.length - 1)) || tok.string.length == 1) ||
tok.type == "tag" && state.type == "closeTag" ||
tok.string.indexOf("/") == (tok.string.length - 1) || // match something like <someTagName />
dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1 ||
closingTagExists(cm, tagName, pos, state, true))
return CodeMirror.Pass;
var indent = indentTags && indexOf(indentTags, lowerTagName) > -1;
replacements[i] = {indent: indent,
text: ">" + (indent ? "\n\n" : "") + "</" + tagName + ">",
newPos: indent ? CodeMirror.Pos(pos.line + 1, 0) : CodeMirror.Pos(pos.line, pos.ch + 1)};
}
var dontIndentOnAutoClose = (typeof opt == "object" && opt.dontIndentOnAutoClose);
for (var i = ranges.length - 1; i >= 0; i--) {
var info = replacements[i];
cm.replaceRange(info.text, ranges[i].head, ranges[i].anchor, "+insert");
var sel = cm.listSelections().slice(0);
sel[i] = {head: info.newPos, anchor: info.newPos};
cm.setSelections(sel);
if (!dontIndentOnAutoClose && info.indent) {
cm.indentLine(info.newPos.line, null, true);
cm.indentLine(info.newPos.line + 1, null, true);
}
}
}
function autoCloseCurrent(cm, typingSlash) {
var ranges = cm.listSelections(), replacements = [];
var head = typingSlash ? "/" : "</";
var opt = cm.getOption("autoCloseTags");
var dontIndentOnAutoClose = (typeof opt == "object" && opt.dontIndentOnSlash);
for (var i = 0; i < ranges.length; i++) {
if (!ranges[i].empty()) return CodeMirror.Pass;
var pos = ranges[i].head, tok = cm.getTokenAt(pos);
var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;
if (typingSlash && (tok.type == "string" || tok.string.charAt(0) != "<" ||
tok.start != pos.ch - 1))
return CodeMirror.Pass;
// Kludge to get around the fact that we are not in XML mode
// when completing in JS/CSS snippet in htmlmixed mode. Does not
// work for other XML embedded languages (there is no general
// way to go from a mixed mode to its current XML state).
var replacement;
if (inner.mode.name != "xml") {
if (cm.getMode().name == "htmlmixed" && inner.mode.name == "javascript")
replacement = head + "script";
else if (cm.getMode().name == "htmlmixed" && inner.mode.name == "css")
replacement = head + "style";
else
return CodeMirror.Pass;
} else {
if (!state.context || !state.context.tagName ||
closingTagExists(cm, state.context.tagName, pos, state))
return CodeMirror.Pass;
replacement = head + state.context.tagName;
}
if (cm.getLine(pos.line).charAt(tok.end) != ">") replacement += ">";
replacements[i] = replacement;
}
cm.replaceSelections(replacements);
ranges = cm.listSelections();
if (!dontIndentOnAutoClose) {
for (var i = 0; i < ranges.length; i++)
if (i == ranges.length - 1 || ranges[i].head.line < ranges[i + 1].head.line)
cm.indentLine(ranges[i].head.line);
}
}
function autoCloseSlash(cm) {
if (cm.getOption("disableInput")) return CodeMirror.Pass;
return autoCloseCurrent(cm, true);
}
CodeMirror.commands.closeTag = function(cm) { return autoCloseCurrent(cm); };
function indexOf(collection, elt) {
if (collection.indexOf) return collection.indexOf(elt);
for (var i = 0, e = collection.length; i < e; ++i)
if (collection[i] == elt) return i;
return -1;
}
// If xml-fold is loaded, we use its functionality to try and verify
// whether a given tag is actually unclosed.
function closingTagExists(cm, tagName, pos, state, newTag) {
if (!CodeMirror.scanForClosingTag) return false;
var end = Math.min(cm.lastLine() + 1, pos.line + 500);
var nextClose = CodeMirror.scanForClosingTag(cm, pos, null, end);
if (!nextClose || nextClose.tag != tagName) return false;
var cx = state.context;
// If the immediate wrapping context contains onCx instances of
// the same tag, a closing tag only exists if there are at least
// that many closing tags of that type following.
for (var onCx = newTag ? 1 : 0; cx && cx.tagName == tagName; cx = cx.prev) ++onCx;
pos = nextClose.to;
for (var i = 1; i < onCx; i++) {
var next = CodeMirror.scanForClosingTag(cm, pos, null, end);
if (!next || next.tag != tagName) return false;
pos = next.to;
}
return true;
}
});

View File

@@ -0,0 +1,89 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
var listRE = /^(\s*)(>[> ]*|[*+-] \[[x ]\]\s|[*+-]\s|(\d+)([.)]))(\s*)/,
emptyListRE = /^(\s*)(>[> ]*|[*+-] \[[x ]\]|[*+-]|(\d+)[.)])(\s*)$/,
unorderedListRE = /[*+-]\s/;
CodeMirror.commands.newlineAndIndentContinueMarkdownList = function(cm) {
if (cm.getOption("disableInput")) return CodeMirror.Pass;
var ranges = cm.listSelections(), replacements = [];
for (var i = 0; i < ranges.length; i++) {
var pos = ranges[i].head;
var eolState = cm.getStateAfter(pos.line);
var inList = eolState.list !== false;
var inQuote = eolState.quote !== 0;
var line = cm.getLine(pos.line), match = listRE.exec(line);
var cursorBeforeBullet = /^\s*$/.test(line.slice(0, pos.ch));
if (!ranges[i].empty() || (!inList && !inQuote) || !match || cursorBeforeBullet) {
cm.execCommand("newlineAndIndent");
return;
}
if (emptyListRE.test(line)) {
if (!/>\s*$/.test(line)) cm.replaceRange("", {
line: pos.line, ch: 0
}, {
line: pos.line, ch: pos.ch + 1
});
replacements[i] = "\n";
} else {
var indent = match[1], after = match[5];
var numbered = !(unorderedListRE.test(match[2]) || match[2].indexOf(">") >= 0);
var bullet = numbered ? (parseInt(match[3], 10) + 1) + match[4] : match[2].replace("x", " ");
replacements[i] = "\n" + indent + bullet + after;
if (numbered) incrementRemainingMarkdownListNumbers(cm, pos);
}
}
cm.replaceSelections(replacements);
};
// Auto-updating Markdown list numbers when a new item is added to the
// middle of a list
function incrementRemainingMarkdownListNumbers(cm, pos) {
var startLine = pos.line, lookAhead = 0, skipCount = 0;
var startItem = listRE.exec(cm.getLine(startLine)), startIndent = startItem[1];
do {
lookAhead += 1;
var nextLineNumber = startLine + lookAhead;
var nextLine = cm.getLine(nextLineNumber), nextItem = listRE.exec(nextLine);
if (nextItem) {
var nextIndent = nextItem[1];
var newNumber = (parseInt(startItem[3], 10) + lookAhead - skipCount);
var nextNumber = (parseInt(nextItem[3], 10)), itemNumber = nextNumber;
if (startIndent === nextIndent && !isNaN(nextNumber)) {
if (newNumber === nextNumber) itemNumber = nextNumber + 1;
if (newNumber > nextNumber) itemNumber = newNumber + 1;
cm.replaceRange(
nextLine.replace(listRE, nextIndent + itemNumber + nextItem[4] + nextItem[5]),
{
line: nextLineNumber, ch: 0
}, {
line: nextLineNumber, ch: nextLine.length
});
} else {
if (startIndent.length > nextIndent.length) return;
// This doesn't run if the next line immediatley indents, as it is
// not clear of the users intention (new indented item or same level)
if ((startIndent.length < nextIndent.length) && (lookAhead === 1)) return;
skipCount += 1;
}
}
} while (nextItem);
}
});

View File

@@ -0,0 +1,145 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
var ie_lt8 = /MSIE \d/.test(navigator.userAgent) &&
(document.documentMode == null || document.documentMode < 8);
var Pos = CodeMirror.Pos;
var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"};
function findMatchingBracket(cm, where, config) {
var line = cm.getLineHandle(where.line), pos = where.ch - 1;
var afterCursor = config && config.afterCursor
if (afterCursor == null)
afterCursor = /(^| )cm-fat-cursor($| )/.test(cm.getWrapperElement().className)
// A cursor is defined as between two characters, but in in vim command mode
// (i.e. not insert mode), the cursor is visually represented as a
// highlighted box on top of the 2nd character. Otherwise, we allow matches
// from before or after the cursor.
var match = (!afterCursor && pos >= 0 && matching[line.text.charAt(pos)]) ||
matching[line.text.charAt(++pos)];
if (!match) return null;
var dir = match.charAt(1) == ">" ? 1 : -1;
if (config && config.strict && (dir > 0) != (pos == where.ch)) return null;
var style = cm.getTokenTypeAt(Pos(where.line, pos + 1));
var found = scanForBracket(cm, Pos(where.line, pos + (dir > 0 ? 1 : 0)), dir, style || null, config);
if (found == null) return null;
return {from: Pos(where.line, pos), to: found && found.pos,
match: found && found.ch == match.charAt(0), forward: dir > 0};
}
// bracketRegex is used to specify which type of bracket to scan
// should be a regexp, e.g. /[[\]]/
//
// Note: If "where" is on an open bracket, then this bracket is ignored.
//
// Returns false when no bracket was found, null when it reached
// maxScanLines and gave up
function scanForBracket(cm, where, dir, style, config) {
var maxScanLen = (config && config.maxScanLineLength) || 10000;
var maxScanLines = (config && config.maxScanLines) || 1000;
var stack = [];
var re = config && config.bracketRegex ? config.bracketRegex : /[(){}[\]]/;
var lineEnd = dir > 0 ? Math.min(where.line + maxScanLines, cm.lastLine() + 1)
: Math.max(cm.firstLine() - 1, where.line - maxScanLines);
for (var lineNo = where.line; lineNo != lineEnd; lineNo += dir) {
var line = cm.getLine(lineNo);
if (!line) continue;
var pos = dir > 0 ? 0 : line.length - 1, end = dir > 0 ? line.length : -1;
if (line.length > maxScanLen) continue;
if (lineNo == where.line) pos = where.ch - (dir < 0 ? 1 : 0);
for (; pos != end; pos += dir) {
var ch = line.charAt(pos);
if (re.test(ch) && (style === undefined || cm.getTokenTypeAt(Pos(lineNo, pos + 1)) == style)) {
var match = matching[ch];
if ((match.charAt(1) == ">") == (dir > 0)) stack.push(ch);
else if (!stack.length) return {pos: Pos(lineNo, pos), ch: ch};
else stack.pop();
}
}
}
return lineNo - dir == (dir > 0 ? cm.lastLine() : cm.firstLine()) ? false : null;
}
function matchBrackets(cm, autoclear, config) {
// Disable brace matching in long lines, since it'll cause hugely slow updates
var maxHighlightLen = cm.state.matchBrackets.maxHighlightLineLength || 1000;
var marks = [], ranges = cm.listSelections();
for (var i = 0; i < ranges.length; i++) {
var match = ranges[i].empty() && findMatchingBracket(cm, ranges[i].head, config);
if (match && cm.getLine(match.from.line).length <= maxHighlightLen) {
var style = match.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
marks.push(cm.markText(match.from, Pos(match.from.line, match.from.ch + 1), {className: style}));
if (match.to && cm.getLine(match.to.line).length <= maxHighlightLen)
marks.push(cm.markText(match.to, Pos(match.to.line, match.to.ch + 1), {className: style}));
}
}
if (marks.length) {
// Kludge to work around the IE bug from issue #1193, where text
// input stops going to the textare whever this fires.
if (ie_lt8 && cm.state.focused) cm.focus();
var clear = function() {
cm.operation(function() {
for (var i = 0; i < marks.length; i++) marks[i].clear();
});
};
if (autoclear) setTimeout(clear, 800);
else return clear;
}
}
function doMatchBrackets(cm) {
cm.operation(function() {
if (cm.state.matchBrackets.currentlyHighlighted) {
cm.state.matchBrackets.currentlyHighlighted();
cm.state.matchBrackets.currentlyHighlighted = null;
}
cm.state.matchBrackets.currentlyHighlighted = matchBrackets(cm, false, cm.state.matchBrackets);
});
}
CodeMirror.defineOption("matchBrackets", false, function(cm, val, old) {
if (old && old != CodeMirror.Init) {
cm.off("cursorActivity", doMatchBrackets);
if (cm.state.matchBrackets && cm.state.matchBrackets.currentlyHighlighted) {
cm.state.matchBrackets.currentlyHighlighted();
cm.state.matchBrackets.currentlyHighlighted = null;
}
}
if (val) {
cm.state.matchBrackets = typeof val == "object" ? val : {};
cm.on("cursorActivity", doMatchBrackets);
}
});
CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);});
CodeMirror.defineExtension("findMatchingBracket", function(pos, config, oldConfig){
// Backwards-compatibility kludge
if (oldConfig || typeof config == "boolean") {
if (!oldConfig) {
config = config ? {strict: true} : null
} else {
oldConfig.strict = config
config = oldConfig
}
}
return findMatchingBracket(this, pos, config)
});
CodeMirror.defineExtension("scanForBracket", function(pos, dir, style, config){
return scanForBracket(this, pos, dir, style, config);
});
});

View File

@@ -0,0 +1,66 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"), require("../fold/xml-fold"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror", "../fold/xml-fold"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
CodeMirror.defineOption("matchTags", false, function(cm, val, old) {
if (old && old != CodeMirror.Init) {
cm.off("cursorActivity", doMatchTags);
cm.off("viewportChange", maybeUpdateMatch);
clear(cm);
}
if (val) {
cm.state.matchBothTags = typeof val == "object" && val.bothTags;
cm.on("cursorActivity", doMatchTags);
cm.on("viewportChange", maybeUpdateMatch);
doMatchTags(cm);
}
});
function clear(cm) {
if (cm.state.tagHit) cm.state.tagHit.clear();
if (cm.state.tagOther) cm.state.tagOther.clear();
cm.state.tagHit = cm.state.tagOther = null;
}
function doMatchTags(cm) {
cm.state.failedTagMatch = false;
cm.operation(function() {
clear(cm);
if (cm.somethingSelected()) return;
var cur = cm.getCursor(), range = cm.getViewport();
range.from = Math.min(range.from, cur.line); range.to = Math.max(cur.line + 1, range.to);
var match = CodeMirror.findMatchingTag(cm, cur, range);
if (!match) return;
if (cm.state.matchBothTags) {
var hit = match.at == "open" ? match.open : match.close;
if (hit) cm.state.tagHit = cm.markText(hit.from, hit.to, {className: "CodeMirror-matchingtag"});
}
var other = match.at == "close" ? match.open : match.close;
if (other)
cm.state.tagOther = cm.markText(other.from, other.to, {className: "CodeMirror-matchingtag"});
else
cm.state.failedTagMatch = true;
});
}
function maybeUpdateMatch(cm) {
if (cm.state.failedTagMatch) doMatchTags(cm);
}
CodeMirror.commands.toMatchingTag = function(cm) {
var found = CodeMirror.findMatchingTag(cm, cm.getCursor());
if (found) {
var other = found.at == "close" ? found.open : found.close;
if (other) cm.extendSelection(other.to, other.from);
}
};
});

View File

@@ -0,0 +1,27 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
CodeMirror.defineOption("showTrailingSpace", false, function(cm, val, prev) {
if (prev == CodeMirror.Init) prev = false;
if (prev && !val)
cm.removeOverlay("trailingspace");
else if (!prev && val)
cm.addOverlay({
token: function(stream) {
for (var l = stream.string.length, i = l; i && /\s/.test(stream.string.charAt(i - 1)); --i) {}
if (i > stream.pos) { stream.pos = i; return null; }
stream.pos = l;
return "trailingspace";
},
name: "trailingspace"
});
});
});

View File

@@ -0,0 +1,105 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
CodeMirror.registerHelper("fold", "brace", function(cm, start) {
var line = start.line, lineText = cm.getLine(line);
var tokenType;
function findOpening(openCh) {
for (var at = start.ch, pass = 0;;) {
var found = at <= 0 ? -1 : lineText.lastIndexOf(openCh, at - 1);
if (found == -1) {
if (pass == 1) break;
pass = 1;
at = lineText.length;
continue;
}
if (pass == 1 && found < start.ch) break;
tokenType = cm.getTokenTypeAt(CodeMirror.Pos(line, found + 1));
if (!/^(comment|string)/.test(tokenType)) return found + 1;
at = found - 1;
}
}
var startToken = "{", endToken = "}", startCh = findOpening("{");
if (startCh == null) {
startToken = "[", endToken = "]";
startCh = findOpening("[");
}
if (startCh == null) return;
var count = 1, lastLine = cm.lastLine(), end, endCh;
outer: for (var i = line; i <= lastLine; ++i) {
var text = cm.getLine(i), pos = i == line ? startCh : 0;
for (;;) {
var nextOpen = text.indexOf(startToken, pos), nextClose = text.indexOf(endToken, pos);
if (nextOpen < 0) nextOpen = text.length;
if (nextClose < 0) nextClose = text.length;
pos = Math.min(nextOpen, nextClose);
if (pos == text.length) break;
if (cm.getTokenTypeAt(CodeMirror.Pos(i, pos + 1)) == tokenType) {
if (pos == nextOpen) ++count;
else if (!--count) { end = i; endCh = pos; break outer; }
}
++pos;
}
}
if (end == null || line == end) return;
return {from: CodeMirror.Pos(line, startCh),
to: CodeMirror.Pos(end, endCh)};
});
CodeMirror.registerHelper("fold", "import", function(cm, start) {
function hasImport(line) {
if (line < cm.firstLine() || line > cm.lastLine()) return null;
var start = cm.getTokenAt(CodeMirror.Pos(line, 1));
if (!/\S/.test(start.string)) start = cm.getTokenAt(CodeMirror.Pos(line, start.end + 1));
if (start.type != "keyword" || start.string != "import") return null;
// Now find closing semicolon, return its position
for (var i = line, e = Math.min(cm.lastLine(), line + 10); i <= e; ++i) {
var text = cm.getLine(i), semi = text.indexOf(";");
if (semi != -1) return {startCh: start.end, end: CodeMirror.Pos(i, semi)};
}
}
var startLine = start.line, has = hasImport(startLine), prev;
if (!has || hasImport(startLine - 1) || ((prev = hasImport(startLine - 2)) && prev.end.line == startLine - 1))
return null;
for (var end = has.end;;) {
var next = hasImport(end.line + 1);
if (next == null) break;
end = next.end;
}
return {from: cm.clipPos(CodeMirror.Pos(startLine, has.startCh + 1)), to: end};
});
CodeMirror.registerHelper("fold", "include", function(cm, start) {
function hasInclude(line) {
if (line < cm.firstLine() || line > cm.lastLine()) return null;
var start = cm.getTokenAt(CodeMirror.Pos(line, 1));
if (!/\S/.test(start.string)) start = cm.getTokenAt(CodeMirror.Pos(line, start.end + 1));
if (start.type == "meta" && start.string.slice(0, 8) == "#include") return start.start + 8;
}
var startLine = start.line, has = hasInclude(startLine);
if (has == null || hasInclude(startLine - 1) != null) return null;
for (var end = startLine;;) {
var next = hasInclude(end + 1);
if (next == null) break;
++end;
}
return {from: CodeMirror.Pos(startLine, has + 1),
to: cm.clipPos(CodeMirror.Pos(end))};
});
});

View File

@@ -0,0 +1,59 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
CodeMirror.registerGlobalHelper("fold", "comment", function(mode) {
return mode.blockCommentStart && mode.blockCommentEnd;
}, function(cm, start) {
var mode = cm.getModeAt(start), startToken = mode.blockCommentStart, endToken = mode.blockCommentEnd;
if (!startToken || !endToken) return;
var line = start.line, lineText = cm.getLine(line);
var startCh;
for (var at = start.ch, pass = 0;;) {
var found = at <= 0 ? -1 : lineText.lastIndexOf(startToken, at - 1);
if (found == -1) {
if (pass == 1) return;
pass = 1;
at = lineText.length;
continue;
}
if (pass == 1 && found < start.ch) return;
if (/comment/.test(cm.getTokenTypeAt(CodeMirror.Pos(line, found + 1))) &&
(found == 0 || lineText.slice(found - endToken.length, found) == endToken ||
!/comment/.test(cm.getTokenTypeAt(CodeMirror.Pos(line, found))))) {
startCh = found + startToken.length;
break;
}
at = found - 1;
}
var depth = 1, lastLine = cm.lastLine(), end, endCh;
outer: for (var i = line; i <= lastLine; ++i) {
var text = cm.getLine(i), pos = i == line ? startCh : 0;
for (;;) {
var nextOpen = text.indexOf(startToken, pos), nextClose = text.indexOf(endToken, pos);
if (nextOpen < 0) nextOpen = text.length;
if (nextClose < 0) nextClose = text.length;
pos = Math.min(nextOpen, nextClose);
if (pos == text.length) break;
if (pos == nextOpen) ++depth;
else if (!--depth) { end = i; endCh = pos; break outer; }
++pos;
}
}
if (end == null || line == end && endCh == startCh) return;
return {from: CodeMirror.Pos(line, startCh),
to: CodeMirror.Pos(end, endCh)};
});
});

View File

@@ -0,0 +1,152 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
function doFold(cm, pos, options, force) {
if (options && options.call) {
var finder = options;
options = null;
} else {
var finder = getOption(cm, options, "rangeFinder");
}
if (typeof pos == "number") pos = CodeMirror.Pos(pos, 0);
var minSize = getOption(cm, options, "minFoldSize");
function getRange(allowFolded) {
var range = finder(cm, pos);
if (!range || range.to.line - range.from.line < minSize) return null;
var marks = cm.findMarksAt(range.from);
for (var i = 0; i < marks.length; ++i) {
if (marks[i].__isFold && force !== "fold") {
if (!allowFolded) return null;
range.cleared = true;
marks[i].clear();
}
}
return range;
}
var range = getRange(true);
if (getOption(cm, options, "scanUp")) while (!range && pos.line > cm.firstLine()) {
pos = CodeMirror.Pos(pos.line - 1, 0);
range = getRange(false);
}
if (!range || range.cleared || force === "unfold") return;
var myWidget = makeWidget(cm, options);
CodeMirror.on(myWidget, "mousedown", function(e) {
myRange.clear();
CodeMirror.e_preventDefault(e);
});
var myRange = cm.markText(range.from, range.to, {
replacedWith: myWidget,
clearOnEnter: getOption(cm, options, "clearOnEnter"),
__isFold: true
});
myRange.on("clear", function(from, to) {
CodeMirror.signal(cm, "unfold", cm, from, to);
});
CodeMirror.signal(cm, "fold", cm, range.from, range.to);
}
function makeWidget(cm, options) {
var widget = getOption(cm, options, "widget");
if (typeof widget == "string") {
var text = document.createTextNode(widget);
widget = document.createElement("span");
widget.appendChild(text);
widget.className = "CodeMirror-foldmarker";
} else if (widget) {
widget = widget.cloneNode(true)
}
return widget;
}
// Clumsy backwards-compatible interface
CodeMirror.newFoldFunction = function(rangeFinder, widget) {
return function(cm, pos) { doFold(cm, pos, {rangeFinder: rangeFinder, widget: widget}); };
};
// New-style interface
CodeMirror.defineExtension("foldCode", function(pos, options, force) {
doFold(this, pos, options, force);
});
CodeMirror.defineExtension("isFolded", function(pos) {
var marks = this.findMarksAt(pos);
for (var i = 0; i < marks.length; ++i)
if (marks[i].__isFold) return true;
});
CodeMirror.commands.toggleFold = function(cm) {
cm.foldCode(cm.getCursor());
};
CodeMirror.commands.fold = function(cm) {
cm.foldCode(cm.getCursor(), null, "fold");
};
CodeMirror.commands.unfold = function(cm) {
cm.foldCode(cm.getCursor(), null, "unfold");
};
CodeMirror.commands.foldAll = function(cm) {
cm.operation(function() {
for (var i = cm.firstLine(), e = cm.lastLine(); i <= e; i++)
cm.foldCode(CodeMirror.Pos(i, 0), null, "fold");
});
};
CodeMirror.commands.unfoldAll = function(cm) {
cm.operation(function() {
for (var i = cm.firstLine(), e = cm.lastLine(); i <= e; i++)
cm.foldCode(CodeMirror.Pos(i, 0), null, "unfold");
});
};
CodeMirror.registerHelper("fold", "combine", function() {
var funcs = Array.prototype.slice.call(arguments, 0);
return function(cm, start) {
for (var i = 0; i < funcs.length; ++i) {
var found = funcs[i](cm, start);
if (found) return found;
}
};
});
CodeMirror.registerHelper("fold", "auto", function(cm, start) {
var helpers = cm.getHelpers(start, "fold");
for (var i = 0; i < helpers.length; i++) {
var cur = helpers[i](cm, start);
if (cur) return cur;
}
});
var defaultOptions = {
rangeFinder: CodeMirror.fold.auto,
widget: "\u2194",
minFoldSize: 0,
scanUp: false,
clearOnEnter: true
};
CodeMirror.defineOption("foldOptions", null);
function getOption(cm, options, name) {
if (options && options[name] !== undefined)
return options[name];
var editorOptions = cm.options.foldOptions;
if (editorOptions && editorOptions[name] !== undefined)
return editorOptions[name];
return defaultOptions[name];
}
CodeMirror.defineExtension("foldOption", function(options, name) {
return getOption(this, options, name);
});
});

View File

@@ -0,0 +1,20 @@
.CodeMirror-foldmarker {
color: blue;
text-shadow: #b9f 1px 1px 2px, #b9f -1px -1px 2px, #b9f 1px -1px 2px, #b9f -1px 1px 2px;
font-family: arial;
line-height: .3;
cursor: pointer;
}
.CodeMirror-foldgutter {
width: .7em;
}
.CodeMirror-foldgutter-open,
.CodeMirror-foldgutter-folded {
cursor: pointer;
}
.CodeMirror-foldgutter-open:after {
content: "\25BE";
}
.CodeMirror-foldgutter-folded:after {
content: "\25B8";
}

View File

@@ -0,0 +1,146 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"), require("./foldcode"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror", "./foldcode"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
CodeMirror.defineOption("foldGutter", false, function(cm, val, old) {
if (old && old != CodeMirror.Init) {
cm.clearGutter(cm.state.foldGutter.options.gutter);
cm.state.foldGutter = null;
cm.off("gutterClick", onGutterClick);
cm.off("change", onChange);
cm.off("viewportChange", onViewportChange);
cm.off("fold", onFold);
cm.off("unfold", onFold);
cm.off("swapDoc", onChange);
}
if (val) {
cm.state.foldGutter = new State(parseOptions(val));
updateInViewport(cm);
cm.on("gutterClick", onGutterClick);
cm.on("change", onChange);
cm.on("viewportChange", onViewportChange);
cm.on("fold", onFold);
cm.on("unfold", onFold);
cm.on("swapDoc", onChange);
}
});
var Pos = CodeMirror.Pos;
function State(options) {
this.options = options;
this.from = this.to = 0;
}
function parseOptions(opts) {
if (opts === true) opts = {};
if (opts.gutter == null) opts.gutter = "CodeMirror-foldgutter";
if (opts.indicatorOpen == null) opts.indicatorOpen = "CodeMirror-foldgutter-open";
if (opts.indicatorFolded == null) opts.indicatorFolded = "CodeMirror-foldgutter-folded";
return opts;
}
function isFolded(cm, line) {
var marks = cm.findMarks(Pos(line, 0), Pos(line + 1, 0));
for (var i = 0; i < marks.length; ++i)
if (marks[i].__isFold && marks[i].find().from.line == line) return marks[i];
}
function marker(spec) {
if (typeof spec == "string") {
var elt = document.createElement("div");
elt.className = spec + " CodeMirror-guttermarker-subtle";
return elt;
} else {
return spec.cloneNode(true);
}
}
function updateFoldInfo(cm, from, to) {
var opts = cm.state.foldGutter.options, cur = from;
var minSize = cm.foldOption(opts, "minFoldSize");
var func = cm.foldOption(opts, "rangeFinder");
cm.eachLine(from, to, function(line) {
var mark = null;
if (isFolded(cm, cur)) {
mark = marker(opts.indicatorFolded);
} else {
var pos = Pos(cur, 0);
var range = func && func(cm, pos);
if (range && range.to.line - range.from.line >= minSize)
mark = marker(opts.indicatorOpen);
}
cm.setGutterMarker(line, opts.gutter, mark);
++cur;
});
}
function updateInViewport(cm) {
var vp = cm.getViewport(), state = cm.state.foldGutter;
if (!state) return;
cm.operation(function() {
updateFoldInfo(cm, vp.from, vp.to);
});
state.from = vp.from; state.to = vp.to;
}
function onGutterClick(cm, line, gutter) {
var state = cm.state.foldGutter;
if (!state) return;
var opts = state.options;
if (gutter != opts.gutter) return;
var folded = isFolded(cm, line);
if (folded) folded.clear();
else cm.foldCode(Pos(line, 0), opts.rangeFinder);
}
function onChange(cm) {
var state = cm.state.foldGutter;
if (!state) return;
var opts = state.options;
state.from = state.to = 0;
clearTimeout(state.changeUpdate);
state.changeUpdate = setTimeout(function() { updateInViewport(cm); }, opts.foldOnChangeTimeSpan || 600);
}
function onViewportChange(cm) {
var state = cm.state.foldGutter;
if (!state) return;
var opts = state.options;
clearTimeout(state.changeUpdate);
state.changeUpdate = setTimeout(function() {
var vp = cm.getViewport();
if (state.from == state.to || vp.from - state.to > 20 || state.from - vp.to > 20) {
updateInViewport(cm);
} else {
cm.operation(function() {
if (vp.from < state.from) {
updateFoldInfo(cm, vp.from, state.from);
state.from = vp.from;
}
if (vp.to > state.to) {
updateFoldInfo(cm, state.to, vp.to);
state.to = vp.to;
}
});
}
}, opts.updateViewportTimeSpan || 400);
}
function onFold(cm, from) {
var state = cm.state.foldGutter;
if (!state) return;
var line = from.line;
if (line >= state.from && line < state.to)
updateFoldInfo(cm, line, line + 1);
}
});

View File

@@ -0,0 +1,48 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
function lineIndent(cm, lineNo) {
var text = cm.getLine(lineNo)
var spaceTo = text.search(/\S/)
if (spaceTo == -1 || /\bcomment\b/.test(cm.getTokenTypeAt(CodeMirror.Pos(lineNo, spaceTo + 1))))
return -1
return CodeMirror.countColumn(text, null, cm.getOption("tabSize"))
}
CodeMirror.registerHelper("fold", "indent", function(cm, start) {
var myIndent = lineIndent(cm, start.line)
if (myIndent < 0) return
var lastLineInFold = null
// Go through lines until we find a line that definitely doesn't belong in
// the block we're folding, or to the end.
for (var i = start.line + 1, end = cm.lastLine(); i <= end; ++i) {
var indent = lineIndent(cm, i)
if (indent == -1) {
} else if (indent > myIndent) {
// Lines with a greater indent are considered part of the block.
lastLineInFold = i;
} else {
// If this line has non-space, non-comment content, and is
// indented less or equal to the start line, it is the start of
// another block.
break;
}
}
if (lastLineInFold) return {
from: CodeMirror.Pos(start.line, cm.getLine(start.line).length),
to: CodeMirror.Pos(lastLineInFold, cm.getLine(lastLineInFold).length)
};
});
});

View File

@@ -0,0 +1,49 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
CodeMirror.registerHelper("fold", "markdown", function(cm, start) {
var maxDepth = 100;
function isHeader(lineNo) {
var tokentype = cm.getTokenTypeAt(CodeMirror.Pos(lineNo, 0));
return tokentype && /\bheader\b/.test(tokentype);
}
function headerLevel(lineNo, line, nextLine) {
var match = line && line.match(/^#+/);
if (match && isHeader(lineNo)) return match[0].length;
match = nextLine && nextLine.match(/^[=\-]+\s*$/);
if (match && isHeader(lineNo + 1)) return nextLine[0] == "=" ? 1 : 2;
return maxDepth;
}
var firstLine = cm.getLine(start.line), nextLine = cm.getLine(start.line + 1);
var level = headerLevel(start.line, firstLine, nextLine);
if (level === maxDepth) return undefined;
var lastLineNo = cm.lastLine();
var end = start.line, nextNextLine = cm.getLine(end + 2);
while (end < lastLineNo) {
if (headerLevel(end + 1, nextLine, nextNextLine) <= level) break;
++end;
nextLine = nextNextLine;
nextNextLine = cm.getLine(end + 2);
}
return {
from: CodeMirror.Pos(start.line, firstLine.length),
to: CodeMirror.Pos(end, cm.getLine(end).length)
};
});
});

View File

@@ -0,0 +1,184 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
var Pos = CodeMirror.Pos;
function cmp(a, b) { return a.line - b.line || a.ch - b.ch; }
var nameStartChar = "A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD";
var nameChar = nameStartChar + "\-\:\.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040";
var xmlTagStart = new RegExp("<(/?)([" + nameStartChar + "][" + nameChar + "]*)", "g");
function Iter(cm, line, ch, range) {
this.line = line; this.ch = ch;
this.cm = cm; this.text = cm.getLine(line);
this.min = range ? Math.max(range.from, cm.firstLine()) : cm.firstLine();
this.max = range ? Math.min(range.to - 1, cm.lastLine()) : cm.lastLine();
}
function tagAt(iter, ch) {
var type = iter.cm.getTokenTypeAt(Pos(iter.line, ch));
return type && /\btag\b/.test(type);
}
function nextLine(iter) {
if (iter.line >= iter.max) return;
iter.ch = 0;
iter.text = iter.cm.getLine(++iter.line);
return true;
}
function prevLine(iter) {
if (iter.line <= iter.min) return;
iter.text = iter.cm.getLine(--iter.line);
iter.ch = iter.text.length;
return true;
}
function toTagEnd(iter) {
for (;;) {
var gt = iter.text.indexOf(">", iter.ch);
if (gt == -1) { if (nextLine(iter)) continue; else return; }
if (!tagAt(iter, gt + 1)) { iter.ch = gt + 1; continue; }
var lastSlash = iter.text.lastIndexOf("/", gt);
var selfClose = lastSlash > -1 && !/\S/.test(iter.text.slice(lastSlash + 1, gt));
iter.ch = gt + 1;
return selfClose ? "selfClose" : "regular";
}
}
function toTagStart(iter) {
for (;;) {
var lt = iter.ch ? iter.text.lastIndexOf("<", iter.ch - 1) : -1;
if (lt == -1) { if (prevLine(iter)) continue; else return; }
if (!tagAt(iter, lt + 1)) { iter.ch = lt; continue; }
xmlTagStart.lastIndex = lt;
iter.ch = lt;
var match = xmlTagStart.exec(iter.text);
if (match && match.index == lt) return match;
}
}
function toNextTag(iter) {
for (;;) {
xmlTagStart.lastIndex = iter.ch;
var found = xmlTagStart.exec(iter.text);
if (!found) { if (nextLine(iter)) continue; else return; }
if (!tagAt(iter, found.index + 1)) { iter.ch = found.index + 1; continue; }
iter.ch = found.index + found[0].length;
return found;
}
}
function toPrevTag(iter) {
for (;;) {
var gt = iter.ch ? iter.text.lastIndexOf(">", iter.ch - 1) : -1;
if (gt == -1) { if (prevLine(iter)) continue; else return; }
if (!tagAt(iter, gt + 1)) { iter.ch = gt; continue; }
var lastSlash = iter.text.lastIndexOf("/", gt);
var selfClose = lastSlash > -1 && !/\S/.test(iter.text.slice(lastSlash + 1, gt));
iter.ch = gt + 1;
return selfClose ? "selfClose" : "regular";
}
}
function findMatchingClose(iter, tag) {
var stack = [];
for (;;) {
var next = toNextTag(iter), end, startLine = iter.line, startCh = iter.ch - (next ? next[0].length : 0);
if (!next || !(end = toTagEnd(iter))) return;
if (end == "selfClose") continue;
if (next[1]) { // closing tag
for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == next[2]) {
stack.length = i;
break;
}
if (i < 0 && (!tag || tag == next[2])) return {
tag: next[2],
from: Pos(startLine, startCh),
to: Pos(iter.line, iter.ch)
};
} else { // opening tag
stack.push(next[2]);
}
}
}
function findMatchingOpen(iter, tag) {
var stack = [];
for (;;) {
var prev = toPrevTag(iter);
if (!prev) return;
if (prev == "selfClose") { toTagStart(iter); continue; }
var endLine = iter.line, endCh = iter.ch;
var start = toTagStart(iter);
if (!start) return;
if (start[1]) { // closing tag
stack.push(start[2]);
} else { // opening tag
for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == start[2]) {
stack.length = i;
break;
}
if (i < 0 && (!tag || tag == start[2])) return {
tag: start[2],
from: Pos(iter.line, iter.ch),
to: Pos(endLine, endCh)
};
}
}
}
CodeMirror.registerHelper("fold", "xml", function(cm, start) {
var iter = new Iter(cm, start.line, 0);
for (;;) {
var openTag = toNextTag(iter)
if (!openTag || iter.line != start.line) return
var end = toTagEnd(iter)
if (!end) return
if (!openTag[1] && end != "selfClose") {
var startPos = Pos(iter.line, iter.ch);
var endPos = findMatchingClose(iter, openTag[2]);
return endPos && cmp(endPos.from, startPos) > 0 ? {from: startPos, to: endPos.from} : null
}
}
});
CodeMirror.findMatchingTag = function(cm, pos, range) {
var iter = new Iter(cm, pos.line, pos.ch, range);
if (iter.text.indexOf(">") == -1 && iter.text.indexOf("<") == -1) return;
var end = toTagEnd(iter), to = end && Pos(iter.line, iter.ch);
var start = end && toTagStart(iter);
if (!end || !start || cmp(iter, pos) > 0) return;
var here = {from: Pos(iter.line, iter.ch), to: to, tag: start[2]};
if (end == "selfClose") return {open: here, close: null, at: "open"};
if (start[1]) { // closing tag
return {open: findMatchingOpen(iter, start[2]), close: here, at: "close"};
} else { // opening tag
iter = new Iter(cm, to.line, to.ch, range);
return {open: here, close: findMatchingClose(iter, start[2]), at: "open"};
}
};
CodeMirror.findEnclosingTag = function(cm, pos, range, tag) {
var iter = new Iter(cm, pos.line, pos.ch, range);
for (;;) {
var open = findMatchingOpen(iter, tag);
if (!open) break;
var forward = new Iter(cm, pos.line, pos.ch, range);
var close = findMatchingClose(forward, open.tag);
if (close) return {open: open, close: close};
}
};
// Used by addon/edit/closetag.js
CodeMirror.scanForClosingTag = function(cm, pos, name, end) {
var iter = new Iter(cm, pos.line, pos.ch, end ? {from: 0, to: end} : null);
return findMatchingClose(iter, name);
};
});

View File

@@ -0,0 +1,41 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
var WORD = /[\w$]+/, RANGE = 500;
CodeMirror.registerHelper("hint", "anyword", function(editor, options) {
var word = options && options.word || WORD;
var range = options && options.range || RANGE;
var cur = editor.getCursor(), curLine = editor.getLine(cur.line);
var end = cur.ch, start = end;
while (start && word.test(curLine.charAt(start - 1))) --start;
var curWord = start != end && curLine.slice(start, end);
var list = options && options.list || [], seen = {};
var re = new RegExp(word.source, "g");
for (var dir = -1; dir <= 1; dir += 2) {
var line = cur.line, endLine = Math.min(Math.max(line + dir * range, editor.firstLine()), editor.lastLine()) + dir;
for (; line != endLine; line += dir) {
var text = editor.getLine(line), m;
while (m = re.exec(text)) {
if (line == cur.line && m[0] === curWord) continue;
if ((!curWord || m[0].lastIndexOf(curWord, 0) == 0) && !Object.prototype.hasOwnProperty.call(seen, m[0])) {
seen[m[0]] = true;
list.push(m[0]);
}
}
}
}
return {list: list, from: CodeMirror.Pos(cur.line, start), to: CodeMirror.Pos(cur.line, end)};
});
});

View File

@@ -0,0 +1,60 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"), require("../../mode/css/css"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror", "../../mode/css/css"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
var pseudoClasses = {link: 1, visited: 1, active: 1, hover: 1, focus: 1,
"first-letter": 1, "first-line": 1, "first-child": 1,
before: 1, after: 1, lang: 1};
CodeMirror.registerHelper("hint", "css", function(cm) {
var cur = cm.getCursor(), token = cm.getTokenAt(cur);
var inner = CodeMirror.innerMode(cm.getMode(), token.state);
if (inner.mode.name != "css") return;
if (token.type == "keyword" && "!important".indexOf(token.string) == 0)
return {list: ["!important"], from: CodeMirror.Pos(cur.line, token.start),
to: CodeMirror.Pos(cur.line, token.end)};
var start = token.start, end = cur.ch, word = token.string.slice(0, end - start);
if (/[^\w$_-]/.test(word)) {
word = ""; start = end = cur.ch;
}
var spec = CodeMirror.resolveMode("text/css");
var result = [];
function add(keywords) {
for (var name in keywords)
if (!word || name.lastIndexOf(word, 0) == 0)
result.push(name);
}
var st = inner.state.state;
if (st == "pseudo" || token.type == "variable-3") {
add(pseudoClasses);
} else if (st == "block" || st == "maybeprop") {
add(spec.propertyKeywords);
} else if (st == "prop" || st == "parens" || st == "at" || st == "params") {
add(spec.valueKeywords);
add(spec.colorKeywords);
} else if (st == "media" || st == "media_parens") {
add(spec.mediaTypes);
add(spec.mediaFeatures);
}
if (result.length) return {
list: result,
from: CodeMirror.Pos(cur.line, start),
to: CodeMirror.Pos(cur.line, end)
};
});
});

View File

@@ -0,0 +1,348 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"), require("./xml-hint"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror", "./xml-hint"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
var langs = "ab aa af ak sq am ar an hy as av ae ay az bm ba eu be bn bh bi bs br bg my ca ch ce ny zh cv kw co cr hr cs da dv nl dz en eo et ee fo fj fi fr ff gl ka de el gn gu ht ha he hz hi ho hu ia id ie ga ig ik io is it iu ja jv kl kn kr ks kk km ki rw ky kv kg ko ku kj la lb lg li ln lo lt lu lv gv mk mg ms ml mt mi mr mh mn na nv nb nd ne ng nn no ii nr oc oj cu om or os pa pi fa pl ps pt qu rm rn ro ru sa sc sd se sm sg sr gd sn si sk sl so st es su sw ss sv ta te tg th ti bo tk tl tn to tr ts tt tw ty ug uk ur uz ve vi vo wa cy wo fy xh yi yo za zu".split(" ");
var targets = ["_blank", "_self", "_top", "_parent"];
var charsets = ["ascii", "utf-8", "utf-16", "latin1", "latin1"];
var methods = ["get", "post", "put", "delete"];
var encs = ["application/x-www-form-urlencoded", "multipart/form-data", "text/plain"];
var media = ["all", "screen", "print", "embossed", "braille", "handheld", "print", "projection", "screen", "tty", "tv", "speech",
"3d-glasses", "resolution [>][<][=] [X]", "device-aspect-ratio: X/Y", "orientation:portrait",
"orientation:landscape", "device-height: [X]", "device-width: [X]"];
var s = { attrs: {} }; // Simple tag, reused for a whole lot of tags
var data = {
a: {
attrs: {
href: null, ping: null, type: null,
media: media,
target: targets,
hreflang: langs
}
},
abbr: s,
acronym: s,
address: s,
applet: s,
area: {
attrs: {
alt: null, coords: null, href: null, target: null, ping: null,
media: media, hreflang: langs, type: null,
shape: ["default", "rect", "circle", "poly"]
}
},
article: s,
aside: s,
audio: {
attrs: {
src: null, mediagroup: null,
crossorigin: ["anonymous", "use-credentials"],
preload: ["none", "metadata", "auto"],
autoplay: ["", "autoplay"],
loop: ["", "loop"],
controls: ["", "controls"]
}
},
b: s,
base: { attrs: { href: null, target: targets } },
basefont: s,
bdi: s,
bdo: s,
big: s,
blockquote: { attrs: { cite: null } },
body: s,
br: s,
button: {
attrs: {
form: null, formaction: null, name: null, value: null,
autofocus: ["", "autofocus"],
disabled: ["", "autofocus"],
formenctype: encs,
formmethod: methods,
formnovalidate: ["", "novalidate"],
formtarget: targets,
type: ["submit", "reset", "button"]
}
},
canvas: { attrs: { width: null, height: null } },
caption: s,
center: s,
cite: s,
code: s,
col: { attrs: { span: null } },
colgroup: { attrs: { span: null } },
command: {
attrs: {
type: ["command", "checkbox", "radio"],
label: null, icon: null, radiogroup: null, command: null, title: null,
disabled: ["", "disabled"],
checked: ["", "checked"]
}
},
data: { attrs: { value: null } },
datagrid: { attrs: { disabled: ["", "disabled"], multiple: ["", "multiple"] } },
datalist: { attrs: { data: null } },
dd: s,
del: { attrs: { cite: null, datetime: null } },
details: { attrs: { open: ["", "open"] } },
dfn: s,
dir: s,
div: s,
dl: s,
dt: s,
em: s,
embed: { attrs: { src: null, type: null, width: null, height: null } },
eventsource: { attrs: { src: null } },
fieldset: { attrs: { disabled: ["", "disabled"], form: null, name: null } },
figcaption: s,
figure: s,
font: s,
footer: s,
form: {
attrs: {
action: null, name: null,
"accept-charset": charsets,
autocomplete: ["on", "off"],
enctype: encs,
method: methods,
novalidate: ["", "novalidate"],
target: targets
}
},
frame: s,
frameset: s,
h1: s, h2: s, h3: s, h4: s, h5: s, h6: s,
head: {
attrs: {},
children: ["title", "base", "link", "style", "meta", "script", "noscript", "command"]
},
header: s,
hgroup: s,
hr: s,
html: {
attrs: { manifest: null },
children: ["head", "body"]
},
i: s,
iframe: {
attrs: {
src: null, srcdoc: null, name: null, width: null, height: null,
sandbox: ["allow-top-navigation", "allow-same-origin", "allow-forms", "allow-scripts"],
seamless: ["", "seamless"]
}
},
img: {
attrs: {
alt: null, src: null, ismap: null, usemap: null, width: null, height: null,
crossorigin: ["anonymous", "use-credentials"]
}
},
input: {
attrs: {
alt: null, dirname: null, form: null, formaction: null,
height: null, list: null, max: null, maxlength: null, min: null,
name: null, pattern: null, placeholder: null, size: null, src: null,
step: null, value: null, width: null,
accept: ["audio/*", "video/*", "image/*"],
autocomplete: ["on", "off"],
autofocus: ["", "autofocus"],
checked: ["", "checked"],
disabled: ["", "disabled"],
formenctype: encs,
formmethod: methods,
formnovalidate: ["", "novalidate"],
formtarget: targets,
multiple: ["", "multiple"],
readonly: ["", "readonly"],
required: ["", "required"],
type: ["hidden", "text", "search", "tel", "url", "email", "password", "datetime", "date", "month",
"week", "time", "datetime-local", "number", "range", "color", "checkbox", "radio",
"file", "submit", "image", "reset", "button"]
}
},
ins: { attrs: { cite: null, datetime: null } },
kbd: s,
keygen: {
attrs: {
challenge: null, form: null, name: null,
autofocus: ["", "autofocus"],
disabled: ["", "disabled"],
keytype: ["RSA"]
}
},
label: { attrs: { "for": null, form: null } },
legend: s,
li: { attrs: { value: null } },
link: {
attrs: {
href: null, type: null,
hreflang: langs,
media: media,
sizes: ["all", "16x16", "16x16 32x32", "16x16 32x32 64x64"]
}
},
map: { attrs: { name: null } },
mark: s,
menu: { attrs: { label: null, type: ["list", "context", "toolbar"] } },
meta: {
attrs: {
content: null,
charset: charsets,
name: ["viewport", "application-name", "author", "description", "generator", "keywords"],
"http-equiv": ["content-language", "content-type", "default-style", "refresh"]
}
},
meter: { attrs: { value: null, min: null, low: null, high: null, max: null, optimum: null } },
nav: s,
noframes: s,
noscript: s,
object: {
attrs: {
data: null, type: null, name: null, usemap: null, form: null, width: null, height: null,
typemustmatch: ["", "typemustmatch"]
}
},
ol: { attrs: { reversed: ["", "reversed"], start: null, type: ["1", "a", "A", "i", "I"] } },
optgroup: { attrs: { disabled: ["", "disabled"], label: null } },
option: { attrs: { disabled: ["", "disabled"], label: null, selected: ["", "selected"], value: null } },
output: { attrs: { "for": null, form: null, name: null } },
p: s,
param: { attrs: { name: null, value: null } },
pre: s,
progress: { attrs: { value: null, max: null } },
q: { attrs: { cite: null } },
rp: s,
rt: s,
ruby: s,
s: s,
samp: s,
script: {
attrs: {
type: ["text/javascript"],
src: null,
async: ["", "async"],
defer: ["", "defer"],
charset: charsets
}
},
section: s,
select: {
attrs: {
form: null, name: null, size: null,
autofocus: ["", "autofocus"],
disabled: ["", "disabled"],
multiple: ["", "multiple"]
}
},
small: s,
source: { attrs: { src: null, type: null, media: null } },
span: s,
strike: s,
strong: s,
style: {
attrs: {
type: ["text/css"],
media: media,
scoped: null
}
},
sub: s,
summary: s,
sup: s,
table: s,
tbody: s,
td: { attrs: { colspan: null, rowspan: null, headers: null } },
textarea: {
attrs: {
dirname: null, form: null, maxlength: null, name: null, placeholder: null,
rows: null, cols: null,
autofocus: ["", "autofocus"],
disabled: ["", "disabled"],
readonly: ["", "readonly"],
required: ["", "required"],
wrap: ["soft", "hard"]
}
},
tfoot: s,
th: { attrs: { colspan: null, rowspan: null, headers: null, scope: ["row", "col", "rowgroup", "colgroup"] } },
thead: s,
time: { attrs: { datetime: null } },
title: s,
tr: s,
track: {
attrs: {
src: null, label: null, "default": null,
kind: ["subtitles", "captions", "descriptions", "chapters", "metadata"],
srclang: langs
}
},
tt: s,
u: s,
ul: s,
"var": s,
video: {
attrs: {
src: null, poster: null, width: null, height: null,
crossorigin: ["anonymous", "use-credentials"],
preload: ["auto", "metadata", "none"],
autoplay: ["", "autoplay"],
mediagroup: ["movie"],
muted: ["", "muted"],
controls: ["", "controls"]
}
},
wbr: s
};
var globalAttrs = {
accesskey: ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"],
"class": null,
contenteditable: ["true", "false"],
contextmenu: null,
dir: ["ltr", "rtl", "auto"],
draggable: ["true", "false", "auto"],
dropzone: ["copy", "move", "link", "string:", "file:"],
hidden: ["hidden"],
id: null,
inert: ["inert"],
itemid: null,
itemprop: null,
itemref: null,
itemscope: ["itemscope"],
itemtype: null,
lang: ["en", "es"],
spellcheck: ["true", "false"],
style: null,
tabindex: ["1", "2", "3", "4", "5", "6", "7", "8", "9"],
title: null,
translate: ["yes", "no"],
onclick: null,
rel: ["stylesheet", "alternate", "author", "bookmark", "help", "license", "next", "nofollow", "noreferrer", "prefetch", "prev", "search", "tag"]
};
function populate(obj) {
for (var attr in globalAttrs) if (globalAttrs.hasOwnProperty(attr))
obj.attrs[attr] = globalAttrs[attr];
}
populate(s);
for (var tag in data) if (data.hasOwnProperty(tag) && data[tag] != s)
populate(data[tag]);
CodeMirror.htmlSchema = data;
function htmlHint(cm, options) {
var local = {schemaInfo: data};
if (options) for (var opt in options) local[opt] = options[opt];
return CodeMirror.hint.xml(cm, local);
}
CodeMirror.registerHelper("hint", "html", htmlHint);
});

View File

@@ -0,0 +1,157 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
var Pos = CodeMirror.Pos;
function forEach(arr, f) {
for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]);
}
function arrayContains(arr, item) {
if (!Array.prototype.indexOf) {
var i = arr.length;
while (i--) {
if (arr[i] === item) {
return true;
}
}
return false;
}
return arr.indexOf(item) != -1;
}
function scriptHint(editor, keywords, getToken, options) {
// Find the token at the cursor
var cur = editor.getCursor(), token = getToken(editor, cur);
if (/\b(?:string|comment)\b/.test(token.type)) return;
var innerMode = CodeMirror.innerMode(editor.getMode(), token.state);
if (innerMode.mode.helperType === "json") return;
token.state = innerMode.state;
// If it's not a 'word-style' token, ignore the token.
if (!/^[\w$_]*$/.test(token.string)) {
token = {start: cur.ch, end: cur.ch, string: "", state: token.state,
type: token.string == "." ? "property" : null};
} else if (token.end > cur.ch) {
token.end = cur.ch;
token.string = token.string.slice(0, cur.ch - token.start);
}
var tprop = token;
// If it is a property, find out what it is a property of.
while (tprop.type == "property") {
tprop = getToken(editor, Pos(cur.line, tprop.start));
if (tprop.string != ".") return;
tprop = getToken(editor, Pos(cur.line, tprop.start));
if (!context) var context = [];
context.push(tprop);
}
return {list: getCompletions(token, context, keywords, options),
from: Pos(cur.line, token.start),
to: Pos(cur.line, token.end)};
}
function javascriptHint(editor, options) {
return scriptHint(editor, javascriptKeywords,
function (e, cur) {return e.getTokenAt(cur);},
options);
};
CodeMirror.registerHelper("hint", "javascript", javascriptHint);
function getCoffeeScriptToken(editor, cur) {
// This getToken, it is for coffeescript, imitates the behavior of
// getTokenAt method in javascript.js, that is, returning "property"
// type and treat "." as indepenent token.
var token = editor.getTokenAt(cur);
if (cur.ch == token.start + 1 && token.string.charAt(0) == '.') {
token.end = token.start;
token.string = '.';
token.type = "property";
}
else if (/^\.[\w$_]*$/.test(token.string)) {
token.type = "property";
token.start++;
token.string = token.string.replace(/\./, '');
}
return token;
}
function coffeescriptHint(editor, options) {
return scriptHint(editor, coffeescriptKeywords, getCoffeeScriptToken, options);
}
CodeMirror.registerHelper("hint", "coffeescript", coffeescriptHint);
var stringProps = ("charAt charCodeAt indexOf lastIndexOf substring substr slice trim trimLeft trimRight " +
"toUpperCase toLowerCase split concat match replace search").split(" ");
var arrayProps = ("length concat join splice push pop shift unshift slice reverse sort indexOf " +
"lastIndexOf every some filter forEach map reduce reduceRight ").split(" ");
var funcProps = "prototype apply call bind".split(" ");
var javascriptKeywords = ("break case catch class const continue debugger default delete do else export extends false finally for function " +
"if in import instanceof new null return super switch this throw true try typeof var void while with yield").split(" ");
var coffeescriptKeywords = ("and break catch class continue delete do else extends false finally for " +
"if in instanceof isnt new no not null of off on or return switch then throw true try typeof until void while with yes").split(" ");
function forAllProps(obj, callback) {
if (!Object.getOwnPropertyNames || !Object.getPrototypeOf) {
for (var name in obj) callback(name)
} else {
for (var o = obj; o; o = Object.getPrototypeOf(o))
Object.getOwnPropertyNames(o).forEach(callback)
}
}
function getCompletions(token, context, keywords, options) {
var found = [], start = token.string, global = options && options.globalScope || window;
function maybeAdd(str) {
if (str.lastIndexOf(start, 0) == 0 && !arrayContains(found, str)) found.push(str);
}
function gatherCompletions(obj) {
if (typeof obj == "string") forEach(stringProps, maybeAdd);
else if (obj instanceof Array) forEach(arrayProps, maybeAdd);
else if (obj instanceof Function) forEach(funcProps, maybeAdd);
forAllProps(obj, maybeAdd)
}
if (context && context.length) {
// If this is a property, see if it belongs to some object we can
// find in the current environment.
var obj = context.pop(), base;
if (obj.type && obj.type.indexOf("variable") === 0) {
if (options && options.additionalContext)
base = options.additionalContext[obj.string];
if (!options || options.useGlobalScope !== false)
base = base || global[obj.string];
} else if (obj.type == "string") {
base = "";
} else if (obj.type == "atom") {
base = 1;
} else if (obj.type == "function") {
if (global.jQuery != null && (obj.string == '$' || obj.string == 'jQuery') &&
(typeof global.jQuery == 'function'))
base = global.jQuery();
else if (global._ != null && (obj.string == '_') && (typeof global._ == 'function'))
base = global._();
}
while (base != null && context.length)
base = base[context.pop().string];
if (base != null) gatherCompletions(base);
} else {
// If not, just look in the global object and any local scope
// (reading into JS mode internals to get at the local and global variables)
for (var v = token.state.localVars; v; v = v.next) maybeAdd(v.name);
for (var v = token.state.globalVars; v; v = v.next) maybeAdd(v.name);
if (!options || options.useGlobalScope !== false)
gatherCompletions(global);
forEach(keywords, maybeAdd);
}
return found;
}
});

View File

@@ -0,0 +1,36 @@
.CodeMirror-hints {
position: absolute;
z-index: 10;
overflow: hidden;
list-style: none;
margin: 0;
padding: 2px;
-webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
-moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
box-shadow: 2px 3px 5px rgba(0,0,0,.2);
border-radius: 3px;
border: 1px solid silver;
background: white;
font-size: 90%;
font-family: monospace;
max-height: 20em;
overflow-y: auto;
}
.CodeMirror-hint {
margin: 0;
padding: 0 4px;
border-radius: 2px;
white-space: pre;
color: black;
cursor: pointer;
}
li.CodeMirror-hint-active {
background: #08f;
color: white;
}

View File

@@ -0,0 +1,434 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
var HINT_ELEMENT_CLASS = "CodeMirror-hint";
var ACTIVE_HINT_ELEMENT_CLASS = "CodeMirror-hint-active";
// This is the old interface, kept around for now to stay
// backwards-compatible.
CodeMirror.showHint = function(cm, getHints, options) {
if (!getHints) return cm.showHint(options);
if (options && options.async) getHints.async = true;
var newOpts = {hint: getHints};
if (options) for (var prop in options) newOpts[prop] = options[prop];
return cm.showHint(newOpts);
};
CodeMirror.defineExtension("showHint", function(options) {
options = parseOptions(this, this.getCursor("start"), options);
var selections = this.listSelections()
if (selections.length > 1) return;
// By default, don't allow completion when something is selected.
// A hint function can have a `supportsSelection` property to
// indicate that it can handle selections.
if (this.somethingSelected()) {
if (!options.hint.supportsSelection) return;
// Don't try with cross-line selections
for (var i = 0; i < selections.length; i++)
if (selections[i].head.line != selections[i].anchor.line) return;
}
if (this.state.completionActive) this.state.completionActive.close();
var completion = this.state.completionActive = new Completion(this, options);
if (!completion.options.hint) return;
CodeMirror.signal(this, "startCompletion", this);
completion.update(true);
});
function Completion(cm, options) {
this.cm = cm;
this.options = options;
this.widget = null;
this.debounce = 0;
this.tick = 0;
this.startPos = this.cm.getCursor("start");
this.startLen = this.cm.getLine(this.startPos.line).length - this.cm.getSelection().length;
var self = this;
cm.on("cursorActivity", this.activityFunc = function() { self.cursorActivity(); });
}
var requestAnimationFrame = window.requestAnimationFrame || function(fn) {
return setTimeout(fn, 1000/60);
};
var cancelAnimationFrame = window.cancelAnimationFrame || clearTimeout;
Completion.prototype = {
close: function() {
if (!this.active()) return;
this.cm.state.completionActive = null;
this.tick = null;
this.cm.off("cursorActivity", this.activityFunc);
if (this.widget && this.data) CodeMirror.signal(this.data, "close");
if (this.widget) this.widget.close();
CodeMirror.signal(this.cm, "endCompletion", this.cm);
},
active: function() {
return this.cm.state.completionActive == this;
},
pick: function(data, i) {
var completion = data.list[i];
if (completion.hint) completion.hint(this.cm, data, completion);
else this.cm.replaceRange(getText(completion), completion.from || data.from,
completion.to || data.to, "complete");
CodeMirror.signal(data, "pick", completion);
this.close();
},
cursorActivity: function() {
if (this.debounce) {
cancelAnimationFrame(this.debounce);
this.debounce = 0;
}
var pos = this.cm.getCursor(), line = this.cm.getLine(pos.line);
if (pos.line != this.startPos.line || line.length - pos.ch != this.startLen - this.startPos.ch ||
pos.ch < this.startPos.ch || this.cm.somethingSelected() ||
(!pos.ch || this.options.closeCharacters.test(line.charAt(pos.ch - 1)))) {
this.close();
} else {
var self = this;
this.debounce = requestAnimationFrame(function() {self.update();});
if (this.widget) this.widget.disable();
}
},
update: function(first) {
if (this.tick == null) return
var self = this, myTick = ++this.tick
fetchHints(this.options.hint, this.cm, this.options, function(data) {
if (self.tick == myTick) self.finishUpdate(data, first)
})
},
finishUpdate: function(data, first) {
if (this.data) CodeMirror.signal(this.data, "update");
var picked = (this.widget && this.widget.picked) || (first && this.options.completeSingle);
if (this.widget) this.widget.close();
this.data = data;
if (data && data.list.length) {
if (picked && data.list.length == 1) {
this.pick(data, 0);
} else {
this.widget = new Widget(this, data);
CodeMirror.signal(data, "shown");
}
}
}
};
function parseOptions(cm, pos, options) {
var editor = cm.options.hintOptions;
var out = {};
for (var prop in defaultOptions) out[prop] = defaultOptions[prop];
if (editor) for (var prop in editor)
if (editor[prop] !== undefined) out[prop] = editor[prop];
if (options) for (var prop in options)
if (options[prop] !== undefined) out[prop] = options[prop];
if (out.hint.resolve) out.hint = out.hint.resolve(cm, pos)
return out;
}
function getText(completion) {
if (typeof completion == "string") return completion;
else return completion.text;
}
function buildKeyMap(completion, handle) {
var baseMap = {
Up: function() {handle.moveFocus(-1);},
Down: function() {handle.moveFocus(1);},
PageUp: function() {handle.moveFocus(-handle.menuSize() + 1, true);},
PageDown: function() {handle.moveFocus(handle.menuSize() - 1, true);},
Home: function() {handle.setFocus(0);},
End: function() {handle.setFocus(handle.length - 1);},
Enter: handle.pick,
Tab: handle.pick,
Esc: handle.close
};
var custom = completion.options.customKeys;
var ourMap = custom ? {} : baseMap;
function addBinding(key, val) {
var bound;
if (typeof val != "string")
bound = function(cm) { return val(cm, handle); };
// This mechanism is deprecated
else if (baseMap.hasOwnProperty(val))
bound = baseMap[val];
else
bound = val;
ourMap[key] = bound;
}
if (custom)
for (var key in custom) if (custom.hasOwnProperty(key))
addBinding(key, custom[key]);
var extra = completion.options.extraKeys;
if (extra)
for (var key in extra) if (extra.hasOwnProperty(key))
addBinding(key, extra[key]);
return ourMap;
}
function getHintElement(hintsElement, el) {
while (el && el != hintsElement) {
if (el.nodeName.toUpperCase() === "LI" && el.parentNode == hintsElement) return el;
el = el.parentNode;
}
}
function Widget(completion, data) {
this.completion = completion;
this.data = data;
this.picked = false;
var widget = this, cm = completion.cm;
var hints = this.hints = document.createElement("ul");
var theme = completion.cm.options.theme;
hints.className = "CodeMirror-hints " + theme;
this.selectedHint = data.selectedHint || 0;
var completions = data.list;
for (var i = 0; i < completions.length; ++i) {
var elt = hints.appendChild(document.createElement("li")), cur = completions[i];
var className = HINT_ELEMENT_CLASS + (i != this.selectedHint ? "" : " " + ACTIVE_HINT_ELEMENT_CLASS);
if (cur.className != null) className = cur.className + " " + className;
elt.className = className;
if (cur.render) cur.render(elt, data, cur);
else elt.appendChild(document.createTextNode(cur.displayText || getText(cur)));
elt.hintId = i;
}
var pos = cm.cursorCoords(completion.options.alignWithWord ? data.from : null);
var left = pos.left, top = pos.bottom, below = true;
hints.style.left = left + "px";
hints.style.top = top + "px";
// If we're at the edge of the screen, then we want the menu to appear on the left of the cursor.
var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth);
var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight);
(completion.options.container || document.body).appendChild(hints);
var box = hints.getBoundingClientRect(), overlapY = box.bottom - winH;
var scrolls = hints.scrollHeight > hints.clientHeight + 1
var startScroll = cm.getScrollInfo();
if (overlapY > 0) {
var height = box.bottom - box.top, curTop = pos.top - (pos.bottom - box.top);
if (curTop - height > 0) { // Fits above cursor
hints.style.top = (top = pos.top - height) + "px";
below = false;
} else if (height > winH) {
hints.style.height = (winH - 5) + "px";
hints.style.top = (top = pos.bottom - box.top) + "px";
var cursor = cm.getCursor();
if (data.from.ch != cursor.ch) {
pos = cm.cursorCoords(cursor);
hints.style.left = (left = pos.left) + "px";
box = hints.getBoundingClientRect();
}
}
}
var overlapX = box.right - winW;
if (overlapX > 0) {
if (box.right - box.left > winW) {
hints.style.width = (winW - 5) + "px";
overlapX -= (box.right - box.left) - winW;
}
hints.style.left = (left = pos.left - overlapX) + "px";
}
if (scrolls) for (var node = hints.firstChild; node; node = node.nextSibling)
node.style.paddingRight = cm.display.nativeBarWidth + "px"
cm.addKeyMap(this.keyMap = buildKeyMap(completion, {
moveFocus: function(n, avoidWrap) { widget.changeActive(widget.selectedHint + n, avoidWrap); },
setFocus: function(n) { widget.changeActive(n); },
menuSize: function() { return widget.screenAmount(); },
length: completions.length,
close: function() { completion.close(); },
pick: function() { widget.pick(); },
data: data
}));
if (completion.options.closeOnUnfocus) {
var closingOnBlur;
cm.on("blur", this.onBlur = function() { closingOnBlur = setTimeout(function() { completion.close(); }, 100); });
cm.on("focus", this.onFocus = function() { clearTimeout(closingOnBlur); });
}
cm.on("scroll", this.onScroll = function() {
var curScroll = cm.getScrollInfo(), editor = cm.getWrapperElement().getBoundingClientRect();
var newTop = top + startScroll.top - curScroll.top;
var point = newTop - (window.pageYOffset || (document.documentElement || document.body).scrollTop);
if (!below) point += hints.offsetHeight;
if (point <= editor.top || point >= editor.bottom) return completion.close();
hints.style.top = newTop + "px";
hints.style.left = (left + startScroll.left - curScroll.left) + "px";
});
CodeMirror.on(hints, "dblclick", function(e) {
var t = getHintElement(hints, e.target || e.srcElement);
if (t && t.hintId != null) {widget.changeActive(t.hintId); widget.pick();}
});
CodeMirror.on(hints, "click", function(e) {
var t = getHintElement(hints, e.target || e.srcElement);
if (t && t.hintId != null) {
widget.changeActive(t.hintId);
if (completion.options.completeOnSingleClick) widget.pick();
}
});
CodeMirror.on(hints, "mousedown", function() {
setTimeout(function(){cm.focus();}, 20);
});
CodeMirror.signal(data, "select", completions[this.selectedHint], hints.childNodes[this.selectedHint]);
return true;
}
Widget.prototype = {
close: function() {
if (this.completion.widget != this) return;
this.completion.widget = null;
this.hints.parentNode.removeChild(this.hints);
this.completion.cm.removeKeyMap(this.keyMap);
var cm = this.completion.cm;
if (this.completion.options.closeOnUnfocus) {
cm.off("blur", this.onBlur);
cm.off("focus", this.onFocus);
}
cm.off("scroll", this.onScroll);
},
disable: function() {
this.completion.cm.removeKeyMap(this.keyMap);
var widget = this;
this.keyMap = {Enter: function() { widget.picked = true; }};
this.completion.cm.addKeyMap(this.keyMap);
},
pick: function() {
this.completion.pick(this.data, this.selectedHint);
},
changeActive: function(i, avoidWrap) {
if (i >= this.data.list.length)
i = avoidWrap ? this.data.list.length - 1 : 0;
else if (i < 0)
i = avoidWrap ? 0 : this.data.list.length - 1;
if (this.selectedHint == i) return;
var node = this.hints.childNodes[this.selectedHint];
if (node) node.className = node.className.replace(" " + ACTIVE_HINT_ELEMENT_CLASS, "");
node = this.hints.childNodes[this.selectedHint = i];
node.className += " " + ACTIVE_HINT_ELEMENT_CLASS;
if (node.offsetTop < this.hints.scrollTop)
this.hints.scrollTop = node.offsetTop - 3;
else if (node.offsetTop + node.offsetHeight > this.hints.scrollTop + this.hints.clientHeight)
this.hints.scrollTop = node.offsetTop + node.offsetHeight - this.hints.clientHeight + 3;
CodeMirror.signal(this.data, "select", this.data.list[this.selectedHint], node);
},
screenAmount: function() {
return Math.floor(this.hints.clientHeight / this.hints.firstChild.offsetHeight) || 1;
}
};
function applicableHelpers(cm, helpers) {
if (!cm.somethingSelected()) return helpers
var result = []
for (var i = 0; i < helpers.length; i++)
if (helpers[i].supportsSelection) result.push(helpers[i])
return result
}
function fetchHints(hint, cm, options, callback) {
if (hint.async) {
hint(cm, callback, options)
} else {
var result = hint(cm, options)
if (result && result.then) result.then(callback)
else callback(result)
}
}
function resolveAutoHints(cm, pos) {
var helpers = cm.getHelpers(pos, "hint"), words
if (helpers.length) {
var resolved = function(cm, callback, options) {
var app = applicableHelpers(cm, helpers);
function run(i) {
if (i == app.length) return callback(null)
fetchHints(app[i], cm, options, function(result) {
if (result && result.list.length > 0) callback(result)
else run(i + 1)
})
}
run(0)
}
resolved.async = true
resolved.supportsSelection = true
return resolved
} else if (words = cm.getHelper(cm.getCursor(), "hintWords")) {
return function(cm) { return CodeMirror.hint.fromList(cm, {words: words}) }
} else if (CodeMirror.hint.anyword) {
return function(cm, options) { return CodeMirror.hint.anyword(cm, options) }
} else {
return function() {}
}
}
CodeMirror.registerHelper("hint", "auto", {
resolve: resolveAutoHints
});
CodeMirror.registerHelper("hint", "fromList", function(cm, options) {
var cur = cm.getCursor(), token = cm.getTokenAt(cur)
var term, from = CodeMirror.Pos(cur.line, token.start), to = cur
if (token.start < cur.ch && /\w/.test(token.string.charAt(cur.ch - token.start - 1))) {
term = token.string.substr(0, cur.ch - token.start)
} else {
term = ""
from = cur
}
var found = [];
for (var i = 0; i < options.words.length; i++) {
var word = options.words[i];
if (word.slice(0, term.length) == term)
found.push(word);
}
if (found.length) return {list: found, from: from, to: to};
});
CodeMirror.commands.autocomplete = CodeMirror.showHint;
var defaultOptions = {
hint: CodeMirror.hint.auto,
completeSingle: true,
alignWithWord: true,
closeCharacters: /[\s()\[\]{};:>,]/,
closeOnUnfocus: true,
completeOnSingleClick: true,
container: null,
customKeys: null,
extraKeys: null
};
CodeMirror.defineOption("hintOptions", null);
});

View File

@@ -0,0 +1,299 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"), require("../../mode/sql/sql"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror", "../../mode/sql/sql"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
var tables;
var defaultTable;
var keywords;
var identifierQuote;
var CONS = {
QUERY_DIV: ";",
ALIAS_KEYWORD: "AS"
};
var Pos = CodeMirror.Pos, cmpPos = CodeMirror.cmpPos;
function isArray(val) { return Object.prototype.toString.call(val) == "[object Array]" }
function getKeywords(editor) {
var mode = editor.doc.modeOption;
if (mode === "sql") mode = "text/x-sql";
return CodeMirror.resolveMode(mode).keywords;
}
function getIdentifierQuote(editor) {
var mode = editor.doc.modeOption;
if (mode === "sql") mode = "text/x-sql";
return CodeMirror.resolveMode(mode).identifierQuote || "`";
}
function getText(item) {
return typeof item == "string" ? item : item.text;
}
function wrapTable(name, value) {
if (isArray(value)) value = {columns: value}
if (!value.text) value.text = name
return value
}
function parseTables(input) {
var result = {}
if (isArray(input)) {
for (var i = input.length - 1; i >= 0; i--) {
var item = input[i]
result[getText(item).toUpperCase()] = wrapTable(getText(item), item)
}
} else if (input) {
for (var name in input)
result[name.toUpperCase()] = wrapTable(name, input[name])
}
return result
}
function getTable(name) {
return tables[name.toUpperCase()]
}
function shallowClone(object) {
var result = {};
for (var key in object) if (object.hasOwnProperty(key))
result[key] = object[key];
return result;
}
function match(string, word) {
var len = string.length;
var sub = getText(word).substr(0, len);
return string.toUpperCase() === sub.toUpperCase();
}
function addMatches(result, search, wordlist, formatter) {
if (isArray(wordlist)) {
for (var i = 0; i < wordlist.length; i++)
if (match(search, wordlist[i])) result.push(formatter(wordlist[i]))
} else {
for (var word in wordlist) if (wordlist.hasOwnProperty(word)) {
var val = wordlist[word]
if (!val || val === true)
val = word
else
val = val.displayText ? {text: val.text, displayText: val.displayText} : val.text
if (match(search, val)) result.push(formatter(val))
}
}
}
function cleanName(name) {
// Get rid name from identifierQuote and preceding dot(.)
if (name.charAt(0) == ".") {
name = name.substr(1);
}
// replace doublicated identifierQuotes with single identifierQuotes
// and remove single identifierQuotes
var nameParts = name.split(identifierQuote+identifierQuote);
for (var i = 0; i < nameParts.length; i++)
nameParts[i] = nameParts[i].replace(new RegExp(identifierQuote,"g"), "");
return nameParts.join(identifierQuote);
}
function insertIdentifierQuotes(name) {
var nameParts = getText(name).split(".");
for (var i = 0; i < nameParts.length; i++)
nameParts[i] = identifierQuote +
// doublicate identifierQuotes
nameParts[i].replace(new RegExp(identifierQuote,"g"), identifierQuote+identifierQuote) +
identifierQuote;
var escaped = nameParts.join(".");
if (typeof name == "string") return escaped;
name = shallowClone(name);
name.text = escaped;
return name;
}
function nameCompletion(cur, token, result, editor) {
// Try to complete table, column names and return start position of completion
var useIdentifierQuotes = false;
var nameParts = [];
var start = token.start;
var cont = true;
while (cont) {
cont = (token.string.charAt(0) == ".");
useIdentifierQuotes = useIdentifierQuotes || (token.string.charAt(0) == identifierQuote);
start = token.start;
nameParts.unshift(cleanName(token.string));
token = editor.getTokenAt(Pos(cur.line, token.start));
if (token.string == ".") {
cont = true;
token = editor.getTokenAt(Pos(cur.line, token.start));
}
}
// Try to complete table names
var string = nameParts.join(".");
addMatches(result, string, tables, function(w) {
return useIdentifierQuotes ? insertIdentifierQuotes(w) : w;
});
// Try to complete columns from defaultTable
addMatches(result, string, defaultTable, function(w) {
return useIdentifierQuotes ? insertIdentifierQuotes(w) : w;
});
// Try to complete columns
string = nameParts.pop();
var table = nameParts.join(".");
var alias = false;
var aliasTable = table;
// Check if table is available. If not, find table by Alias
if (!getTable(table)) {
var oldTable = table;
table = findTableByAlias(table, editor);
if (table !== oldTable) alias = true;
}
var columns = getTable(table);
if (columns && columns.columns)
columns = columns.columns;
if (columns) {
addMatches(result, string, columns, function(w) {
var tableInsert = table;
if (alias == true) tableInsert = aliasTable;
if (typeof w == "string") {
w = tableInsert + "." + w;
} else {
w = shallowClone(w);
w.text = tableInsert + "." + w.text;
}
return useIdentifierQuotes ? insertIdentifierQuotes(w) : w;
});
}
return start;
}
function eachWord(lineText, f) {
var words = lineText.split(/\s+/)
for (var i = 0; i < words.length; i++)
if (words[i]) f(words[i].replace(/[,;]/g, ''))
}
function findTableByAlias(alias, editor) {
var doc = editor.doc;
var fullQuery = doc.getValue();
var aliasUpperCase = alias.toUpperCase();
var previousWord = "";
var table = "";
var separator = [];
var validRange = {
start: Pos(0, 0),
end: Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).length)
};
//add separator
var indexOfSeparator = fullQuery.indexOf(CONS.QUERY_DIV);
while(indexOfSeparator != -1) {
separator.push(doc.posFromIndex(indexOfSeparator));
indexOfSeparator = fullQuery.indexOf(CONS.QUERY_DIV, indexOfSeparator+1);
}
separator.unshift(Pos(0, 0));
separator.push(Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).text.length));
//find valid range
var prevItem = null;
var current = editor.getCursor()
for (var i = 0; i < separator.length; i++) {
if ((prevItem == null || cmpPos(current, prevItem) > 0) && cmpPos(current, separator[i]) <= 0) {
validRange = {start: prevItem, end: separator[i]};
break;
}
prevItem = separator[i];
}
if (validRange.start) {
var query = doc.getRange(validRange.start, validRange.end, false);
for (var i = 0; i < query.length; i++) {
var lineText = query[i];
eachWord(lineText, function(word) {
var wordUpperCase = word.toUpperCase();
if (wordUpperCase === aliasUpperCase && getTable(previousWord))
table = previousWord;
if (wordUpperCase !== CONS.ALIAS_KEYWORD)
previousWord = word;
});
if (table) break;
}
}
return table;
}
CodeMirror.registerHelper("hint", "sql", function(editor, options) {
tables = parseTables(options && options.tables)
var defaultTableName = options && options.defaultTable;
var disableKeywords = options && options.disableKeywords;
defaultTable = defaultTableName && getTable(defaultTableName);
keywords = getKeywords(editor);
identifierQuote = getIdentifierQuote(editor);
if (defaultTableName && !defaultTable)
defaultTable = findTableByAlias(defaultTableName, editor);
defaultTable = defaultTable || [];
if (defaultTable.columns)
defaultTable = defaultTable.columns;
var cur = editor.getCursor();
var result = [];
var token = editor.getTokenAt(cur), start, end, search;
if (token.end > cur.ch) {
token.end = cur.ch;
token.string = token.string.slice(0, cur.ch - token.start);
}
if (token.string.match(/^[.`"\w@]\w*$/)) {
search = token.string;
start = token.start;
end = token.end;
} else {
start = end = cur.ch;
search = "";
}
if (search.charAt(0) == "." || search.charAt(0) == identifierQuote) {
start = nameCompletion(cur, token, result, editor);
} else {
addMatches(result, search, defaultTable, function(w) {return {text:w, className: "CodeMirror-hint-table CodeMirror-hint-default-table"};});
addMatches(
result,
search,
tables,
function(w) {
if (typeof w === 'object') {
w.className = "CodeMirror-hint-table";
} else {
w = {text: w, className: "CodeMirror-hint-table"};
}
return w;
}
);
if (!disableKeywords)
addMatches(result, search, keywords, function(w) {return {text: w.toUpperCase(), className: "CodeMirror-hint-keyword"};});
}
return {list: result, from: Pos(cur.line, start), to: Pos(cur.line, end)};
});
});

View File

@@ -0,0 +1,110 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
var Pos = CodeMirror.Pos;
function getHints(cm, options) {
var tags = options && options.schemaInfo;
var quote = (options && options.quoteChar) || '"';
if (!tags) return;
var cur = cm.getCursor(), token = cm.getTokenAt(cur);
if (token.end > cur.ch) {
token.end = cur.ch;
token.string = token.string.slice(0, cur.ch - token.start);
}
var inner = CodeMirror.innerMode(cm.getMode(), token.state);
if (inner.mode.name != "xml") return;
var result = [], replaceToken = false, prefix;
var tag = /\btag\b/.test(token.type) && !/>$/.test(token.string);
var tagName = tag && /^\w/.test(token.string), tagStart;
if (tagName) {
var before = cm.getLine(cur.line).slice(Math.max(0, token.start - 2), token.start);
var tagType = /<\/$/.test(before) ? "close" : /<$/.test(before) ? "open" : null;
if (tagType) tagStart = token.start - (tagType == "close" ? 2 : 1);
} else if (tag && token.string == "<") {
tagType = "open";
} else if (tag && token.string == "</") {
tagType = "close";
}
if (!tag && !inner.state.tagName || tagType) {
if (tagName)
prefix = token.string;
replaceToken = tagType;
var cx = inner.state.context, curTag = cx && tags[cx.tagName];
var childList = cx ? curTag && curTag.children : tags["!top"];
if (childList && tagType != "close") {
for (var i = 0; i < childList.length; ++i) if (!prefix || childList[i].lastIndexOf(prefix, 0) == 0)
result.push("<" + childList[i]);
} else if (tagType != "close") {
for (var name in tags)
if (tags.hasOwnProperty(name) && name != "!top" && name != "!attrs" && (!prefix || name.lastIndexOf(prefix, 0) == 0))
result.push("<" + name);
}
if (cx && (!prefix || tagType == "close" && cx.tagName.lastIndexOf(prefix, 0) == 0))
result.push("</" + cx.tagName + ">");
} else {
// Attribute completion
var curTag = tags[inner.state.tagName], attrs = curTag && curTag.attrs;
var globalAttrs = tags["!attrs"];
if (!attrs && !globalAttrs) return;
if (!attrs) {
attrs = globalAttrs;
} else if (globalAttrs) { // Combine tag-local and global attributes
var set = {};
for (var nm in globalAttrs) if (globalAttrs.hasOwnProperty(nm)) set[nm] = globalAttrs[nm];
for (var nm in attrs) if (attrs.hasOwnProperty(nm)) set[nm] = attrs[nm];
attrs = set;
}
if (token.type == "string" || token.string == "=") { // A value
var before = cm.getRange(Pos(cur.line, Math.max(0, cur.ch - 60)),
Pos(cur.line, token.type == "string" ? token.start : token.end));
var atName = before.match(/([^\s\u00a0=<>\"\']+)=$/), atValues;
if (!atName || !attrs.hasOwnProperty(atName[1]) || !(atValues = attrs[atName[1]])) return;
if (typeof atValues == 'function') atValues = atValues.call(this, cm); // Functions can be used to supply values for autocomplete widget
if (token.type == "string") {
prefix = token.string;
var n = 0;
if (/['"]/.test(token.string.charAt(0))) {
quote = token.string.charAt(0);
prefix = token.string.slice(1);
n++;
}
var len = token.string.length;
if (/['"]/.test(token.string.charAt(len - 1))) {
quote = token.string.charAt(len - 1);
prefix = token.string.substr(n, len - 2);
}
replaceToken = true;
}
for (var i = 0; i < atValues.length; ++i) if (!prefix || atValues[i].lastIndexOf(prefix, 0) == 0)
result.push(quote + atValues[i] + quote);
} else { // An attribute name
if (token.type == "attribute") {
prefix = token.string;
replaceToken = true;
}
for (var attr in attrs) if (attrs.hasOwnProperty(attr) && (!prefix || attr.lastIndexOf(prefix, 0) == 0))
result.push(attr);
}
}
return {
list: result,
from: replaceToken ? Pos(cur.line, tagStart == null ? token.start : tagStart) : cur,
to: replaceToken ? Pos(cur.line, token.end) : cur
};
}
CodeMirror.registerHelper("hint", "xml", getHints);
});

View File

@@ -0,0 +1,47 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
// Depends on coffeelint.js from http://www.coffeelint.org/js/coffeelint.js
// declare global: coffeelint
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
CodeMirror.registerHelper("lint", "coffeescript", function(text) {
var found = [];
if (!window.coffeelint) {
if (window.console) {
window.console.error("Error: window.coffeelint not defined, CodeMirror CoffeeScript linting cannot run.");
}
return found;
}
var parseError = function(err) {
var loc = err.lineNumber;
found.push({from: CodeMirror.Pos(loc-1, 0),
to: CodeMirror.Pos(loc, 0),
severity: err.level,
message: err.message});
};
try {
var res = coffeelint.lint(text);
for(var i = 0; i < res.length; i++) {
parseError(res[i]);
}
} catch(e) {
found.push({from: CodeMirror.Pos(e.location.first_line, 0),
to: CodeMirror.Pos(e.location.last_line, e.location.last_column),
severity: 'error',
message: e.message});
}
return found;
});
});

View File

@@ -0,0 +1,40 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
// Depends on csslint.js from https://github.com/stubbornella/csslint
// declare global: CSSLint
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
CodeMirror.registerHelper("lint", "css", function(text, options) {
var found = [];
if (!window.CSSLint) {
if (window.console) {
window.console.error("Error: window.CSSLint not defined, CodeMirror CSS linting cannot run.");
}
return found;
}
var results = CSSLint.verify(text, options), messages = results.messages, message = null;
for ( var i = 0; i < messages.length; i++) {
message = messages[i];
var startLine = message.line -1, endLine = message.line -1, startCol = message.col -1, endCol = message.col;
found.push({
from: CodeMirror.Pos(startLine, startCol),
to: CodeMirror.Pos(endLine, endCol),
message: message.message,
severity : message.type
});
}
return found;
});
});

View File

@@ -0,0 +1,53 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
// Depends on htmlhint.js from http://htmlhint.com/js/htmlhint.js
// declare global: HTMLHint
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"), require("htmlhint"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror", "htmlhint"], mod);
else // Plain browser env
mod(CodeMirror, window.HTMLHint);
})(function(CodeMirror, HTMLHint) {
"use strict";
var defaultRules = {
"tagname-lowercase": true,
"attr-lowercase": true,
"attr-value-double-quotes": true,
"doctype-first": false,
"tag-pair": true,
"spec-char-escape": true,
"id-unique": true,
"src-not-empty": true,
"attr-no-duplication": true
};
CodeMirror.registerHelper("lint", "html", function(text, options) {
var found = [];
if (HTMLHint && !HTMLHint.verify) HTMLHint = HTMLHint.HTMLHint;
if (!HTMLHint) HTMLHint = window.HTMLHint;
if (!HTMLHint) {
if (window.console) {
window.console.error("Error: HTMLHint not found, not defined on window, or not available through define/require, CodeMirror HTML linting cannot run.");
}
return found;
}
var messages = HTMLHint.verify(text, options && options.rules || defaultRules);
for (var i = 0; i < messages.length; i++) {
var message = messages[i];
var startLine = message.line - 1, endLine = message.line - 1, startCol = message.col - 1, endCol = message.col;
found.push({
from: CodeMirror.Pos(startLine, startCol),
to: CodeMirror.Pos(endLine, endCol),
message: message.message,
severity : message.type
});
}
return found;
});
});

View File

@@ -0,0 +1,63 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
// declare global: JSHINT
function validator(text, options) {
if (!window.JSHINT) {
if (window.console) {
window.console.error("Error: window.JSHINT not defined, CodeMirror JavaScript linting cannot run.");
}
return [];
}
if (!options.indent) // JSHint error.character actually is a column index, this fixes underlining on lines using tabs for indentation
options.indent = 1; // JSHint default value is 4
JSHINT(text, options, options.globals);
var errors = JSHINT.data().errors, result = [];
if (errors) parseErrors(errors, result);
return result;
}
CodeMirror.registerHelper("lint", "javascript", validator);
function parseErrors(errors, output) {
for ( var i = 0; i < errors.length; i++) {
var error = errors[i];
if (error) {
if (error.line <= 0) {
if (window.console) {
window.console.warn("Cannot display JSHint error (invalid line " + error.line + ")", error);
}
continue;
}
var start = error.character - 1, end = start + 1;
if (error.evidence) {
var index = error.evidence.substring(start).search(/.\b/);
if (index > -1) {
end += index;
}
}
// Convert to format expected by validation service
var hint = {
message: error.reason,
severity: error.code ? (error.code.startsWith('W') ? "warning" : "error") : "error",
from: CodeMirror.Pos(error.line - 1, start),
to: CodeMirror.Pos(error.line - 1, end)
};
output.push(hint);
}
}
}
});

View File

@@ -0,0 +1,40 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
// Depends on jsonlint.js from https://github.com/zaach/jsonlint
// declare global: jsonlint
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
CodeMirror.registerHelper("lint", "json", function(text) {
var found = [];
if (!window.jsonlint) {
if (window.console) {
window.console.error("Error: window.jsonlint not defined, CodeMirror JSON linting cannot run.");
}
return found;
}
// for jsonlint's web dist jsonlint is exported as an object with a single property parser, of which parseError
// is a subproperty
var jsonlint = window.jsonlint.parser || window.jsonlint
jsonlint.parseError = function(str, hash) {
var loc = hash.loc;
found.push({from: CodeMirror.Pos(loc.first_line - 1, loc.first_column),
to: CodeMirror.Pos(loc.last_line - 1, loc.last_column),
message: str});
};
try { jsonlint.parse(text); }
catch(e) {}
return found;
});
});

View File

@@ -0,0 +1,73 @@
/* The lint marker gutter */
.CodeMirror-lint-markers {
width: 16px;
}
.CodeMirror-lint-tooltip {
background-color: #ffd;
border: 1px solid black;
border-radius: 4px 4px 4px 4px;
color: black;
font-family: monospace;
font-size: 10pt;
overflow: hidden;
padding: 2px 5px;
position: fixed;
white-space: pre;
white-space: pre-wrap;
z-index: 100;
max-width: 600px;
opacity: 0;
transition: opacity .4s;
-moz-transition: opacity .4s;
-webkit-transition: opacity .4s;
-o-transition: opacity .4s;
-ms-transition: opacity .4s;
}
.CodeMirror-lint-mark-error, .CodeMirror-lint-mark-warning {
background-position: left bottom;
background-repeat: repeat-x;
}
.CodeMirror-lint-mark-error {
background-image:
url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDw4cOCW1/KIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAHElEQVQI12NggIL/DAz/GdA5/xkY/qPKMDAwAADLZwf5rvm+LQAAAABJRU5ErkJggg==")
;
}
.CodeMirror-lint-mark-warning {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJFhQXEbhTg7YAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII=");
}
.CodeMirror-lint-marker-error, .CodeMirror-lint-marker-warning {
background-position: center center;
background-repeat: no-repeat;
cursor: pointer;
display: inline-block;
height: 16px;
width: 16px;
vertical-align: middle;
position: relative;
}
.CodeMirror-lint-message-error, .CodeMirror-lint-message-warning {
padding-left: 18px;
background-position: top left;
background-repeat: no-repeat;
}
.CodeMirror-lint-marker-error, .CodeMirror-lint-message-error {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAHlBMVEW7AAC7AACxAAC7AAC7AAAAAAC4AAC5AAD///+7AAAUdclpAAAABnRSTlMXnORSiwCK0ZKSAAAATUlEQVR42mWPOQ7AQAgDuQLx/z8csYRmPRIFIwRGnosRrpamvkKi0FTIiMASR3hhKW+hAN6/tIWhu9PDWiTGNEkTtIOucA5Oyr9ckPgAWm0GPBog6v4AAAAASUVORK5CYII=");
}
.CodeMirror-lint-marker-warning, .CodeMirror-lint-message-warning {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAANlBMVEX/uwDvrwD/uwD/uwD/uwD/uwD/uwD/uwD/uwD6twD/uwAAAADurwD2tQD7uAD+ugAAAAD/uwDhmeTRAAAADHRSTlMJ8mN1EYcbmiixgACm7WbuAAAAVklEQVR42n3PUQqAIBBFUU1LLc3u/jdbOJoW1P08DA9Gba8+YWJ6gNJoNYIBzAA2chBth5kLmG9YUoG0NHAUwFXwO9LuBQL1giCQb8gC9Oro2vp5rncCIY8L8uEx5ZkAAAAASUVORK5CYII=");
}
.CodeMirror-lint-marker-multiple {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAMAAADzjKfhAAAACVBMVEUAAAAAAAC/v7914kyHAAAAAXRSTlMAQObYZgAAACNJREFUeNo1ioEJAAAIwmz/H90iFFSGJgFMe3gaLZ0od+9/AQZ0ADosbYraAAAAAElFTkSuQmCC");
background-repeat: no-repeat;
background-position: right bottom;
width: 100%; height: 100%;
}

View File

@@ -0,0 +1,252 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
var GUTTER_ID = "CodeMirror-lint-markers";
function showTooltip(e, content) {
var tt = document.createElement("div");
tt.className = "CodeMirror-lint-tooltip";
tt.appendChild(content.cloneNode(true));
document.body.appendChild(tt);
function position(e) {
if (!tt.parentNode) return CodeMirror.off(document, "mousemove", position);
tt.style.top = Math.max(0, e.clientY - tt.offsetHeight - 5) + "px";
tt.style.left = (e.clientX + 5) + "px";
}
CodeMirror.on(document, "mousemove", position);
position(e);
if (tt.style.opacity != null) tt.style.opacity = 1;
return tt;
}
function rm(elt) {
if (elt.parentNode) elt.parentNode.removeChild(elt);
}
function hideTooltip(tt) {
if (!tt.parentNode) return;
if (tt.style.opacity == null) rm(tt);
tt.style.opacity = 0;
setTimeout(function() { rm(tt); }, 600);
}
function showTooltipFor(e, content, node) {
var tooltip = showTooltip(e, content);
function hide() {
CodeMirror.off(node, "mouseout", hide);
if (tooltip) { hideTooltip(tooltip); tooltip = null; }
}
var poll = setInterval(function() {
if (tooltip) for (var n = node;; n = n.parentNode) {
if (n && n.nodeType == 11) n = n.host;
if (n == document.body) return;
if (!n) { hide(); break; }
}
if (!tooltip) return clearInterval(poll);
}, 400);
CodeMirror.on(node, "mouseout", hide);
}
function LintState(cm, options, hasGutter) {
this.marked = [];
this.options = options;
this.timeout = null;
this.hasGutter = hasGutter;
this.onMouseOver = function(e) { onMouseOver(cm, e); };
this.waitingFor = 0
}
function parseOptions(_cm, options) {
if (options instanceof Function) return {getAnnotations: options};
if (!options || options === true) options = {};
return options;
}
function clearMarks(cm) {
var state = cm.state.lint;
if (state.hasGutter) cm.clearGutter(GUTTER_ID);
for (var i = 0; i < state.marked.length; ++i)
state.marked[i].clear();
state.marked.length = 0;
}
function makeMarker(labels, severity, multiple, tooltips) {
var marker = document.createElement("div"), inner = marker;
marker.className = "CodeMirror-lint-marker-" + severity;
if (multiple) {
inner = marker.appendChild(document.createElement("div"));
inner.className = "CodeMirror-lint-marker-multiple";
}
if (tooltips != false) CodeMirror.on(inner, "mouseover", function(e) {
showTooltipFor(e, labels, inner);
});
return marker;
}
function getMaxSeverity(a, b) {
if (a == "error") return a;
else return b;
}
function groupByLine(annotations) {
var lines = [];
for (var i = 0; i < annotations.length; ++i) {
var ann = annotations[i], line = ann.from.line;
(lines[line] || (lines[line] = [])).push(ann);
}
return lines;
}
function annotationTooltip(ann) {
var severity = ann.severity;
if (!severity) severity = "error";
var tip = document.createElement("div");
tip.className = "CodeMirror-lint-message-" + severity;
if (typeof ann.messageHTML != 'undefined') {
tip.innerHTML = ann.messageHTML;
} else {
tip.appendChild(document.createTextNode(ann.message));
}
return tip;
}
function lintAsync(cm, getAnnotations, passOptions) {
var state = cm.state.lint
var id = ++state.waitingFor
function abort() {
id = -1
cm.off("change", abort)
}
cm.on("change", abort)
getAnnotations(cm.getValue(), function(annotations, arg2) {
cm.off("change", abort)
if (state.waitingFor != id) return
if (arg2 && annotations instanceof CodeMirror) annotations = arg2
cm.operation(function() {updateLinting(cm, annotations)})
}, passOptions, cm);
}
function startLinting(cm) {
var state = cm.state.lint, options = state.options;
/*
* Passing rules in `options` property prevents JSHint (and other linters) from complaining
* about unrecognized rules like `onUpdateLinting`, `delay`, `lintOnChange`, etc.
*/
var passOptions = options.options || options;
var getAnnotations = options.getAnnotations || cm.getHelper(CodeMirror.Pos(0, 0), "lint");
if (!getAnnotations) return;
if (options.async || getAnnotations.async) {
lintAsync(cm, getAnnotations, passOptions)
} else {
var annotations = getAnnotations(cm.getValue(), passOptions, cm);
if (!annotations) return;
if (annotations.then) annotations.then(function(issues) {
cm.operation(function() {updateLinting(cm, issues)})
});
else cm.operation(function() {updateLinting(cm, annotations)})
}
}
function updateLinting(cm, annotationsNotSorted) {
clearMarks(cm);
var state = cm.state.lint, options = state.options;
var annotations = groupByLine(annotationsNotSorted);
for (var line = 0; line < annotations.length; ++line) {
var anns = annotations[line];
if (!anns) continue;
var maxSeverity = null;
var tipLabel = state.hasGutter && document.createDocumentFragment();
for (var i = 0; i < anns.length; ++i) {
var ann = anns[i];
var severity = ann.severity;
if (!severity) severity = "error";
maxSeverity = getMaxSeverity(maxSeverity, severity);
if (options.formatAnnotation) ann = options.formatAnnotation(ann);
if (state.hasGutter) tipLabel.appendChild(annotationTooltip(ann));
if (ann.to) state.marked.push(cm.markText(ann.from, ann.to, {
className: "CodeMirror-lint-mark-" + severity,
__annotation: ann
}));
}
if (state.hasGutter)
cm.setGutterMarker(line, GUTTER_ID, makeMarker(tipLabel, maxSeverity, anns.length > 1,
state.options.tooltips));
}
if (options.onUpdateLinting) options.onUpdateLinting(annotationsNotSorted, annotations, cm);
}
function onChange(cm) {
var state = cm.state.lint;
if (!state) return;
clearTimeout(state.timeout);
state.timeout = setTimeout(function(){startLinting(cm);}, state.options.delay || 500);
}
function popupTooltips(annotations, e) {
var target = e.target || e.srcElement;
var tooltip = document.createDocumentFragment();
for (var i = 0; i < annotations.length; i++) {
var ann = annotations[i];
tooltip.appendChild(annotationTooltip(ann));
}
showTooltipFor(e, tooltip, target);
}
function onMouseOver(cm, e) {
var target = e.target || e.srcElement;
if (!/\bCodeMirror-lint-mark-/.test(target.className)) return;
var box = target.getBoundingClientRect(), x = (box.left + box.right) / 2, y = (box.top + box.bottom) / 2;
var spans = cm.findMarksAt(cm.coordsChar({left: x, top: y}, "client"));
var annotations = [];
for (var i = 0; i < spans.length; ++i) {
var ann = spans[i].__annotation;
if (ann) annotations.push(ann);
}
if (annotations.length) popupTooltips(annotations, e);
}
CodeMirror.defineOption("lint", false, function(cm, val, old) {
if (old && old != CodeMirror.Init) {
clearMarks(cm);
if (cm.state.lint.options.lintOnChange !== false)
cm.off("change", onChange);
CodeMirror.off(cm.getWrapperElement(), "mouseover", cm.state.lint.onMouseOver);
clearTimeout(cm.state.lint.timeout);
delete cm.state.lint;
}
if (val) {
var gutters = cm.getOption("gutters"), hasLintGutter = false;
for (var i = 0; i < gutters.length; ++i) if (gutters[i] == GUTTER_ID) hasLintGutter = true;
var state = cm.state.lint = new LintState(cm, parseOptions(cm, val), hasLintGutter);
if (state.options.lintOnChange !== false)
cm.on("change", onChange);
if (state.options.tooltips != false && state.options.tooltips != "gutter")
CodeMirror.on(cm.getWrapperElement(), "mouseover", state.onMouseOver);
startLinting(cm);
}
});
CodeMirror.defineExtension("performLint", function() {
if (this.state.lint) startLinting(this);
});
});

View File

@@ -0,0 +1,41 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
// Depends on js-yaml.js from https://github.com/nodeca/js-yaml
// declare global: jsyaml
CodeMirror.registerHelper("lint", "yaml", function(text) {
var found = [];
if (!window.jsyaml) {
if (window.console) {
window.console.error("Error: window.jsyaml not defined, CodeMirror YAML linting cannot run.");
}
return found;
}
try { jsyaml.load(text); }
catch(e) {
var loc = e.mark,
// js-yaml YAMLException doesn't always provide an accurate lineno
// e.g., when there are multiple yaml docs
// ---
// ---
// foo:bar
from = loc ? CodeMirror.Pos(loc.line, loc.column) : CodeMirror.Pos(0, 0),
to = from;
found.push({ from: from, to: to, message: e.message });
}
return found;
});
});

View File

@@ -0,0 +1,119 @@
.CodeMirror-merge {
position: relative;
border: 1px solid #ddd;
white-space: pre;
}
.CodeMirror-merge, .CodeMirror-merge .CodeMirror {
height: 350px;
}
.CodeMirror-merge-2pane .CodeMirror-merge-pane { width: 47%; }
.CodeMirror-merge-2pane .CodeMirror-merge-gap { width: 6%; }
.CodeMirror-merge-3pane .CodeMirror-merge-pane { width: 31%; }
.CodeMirror-merge-3pane .CodeMirror-merge-gap { width: 3.5%; }
.CodeMirror-merge-pane {
display: inline-block;
white-space: normal;
vertical-align: top;
}
.CodeMirror-merge-pane-rightmost {
position: absolute;
right: 0px;
z-index: 1;
}
.CodeMirror-merge-gap {
z-index: 2;
display: inline-block;
height: 100%;
-moz-box-sizing: border-box;
box-sizing: border-box;
overflow: hidden;
border-left: 1px solid #ddd;
border-right: 1px solid #ddd;
position: relative;
background: #f8f8f8;
}
.CodeMirror-merge-scrolllock-wrap {
position: absolute;
bottom: 0; left: 50%;
}
.CodeMirror-merge-scrolllock {
position: relative;
left: -50%;
cursor: pointer;
color: #555;
line-height: 1;
}
.CodeMirror-merge-scrolllock:after {
content: "\21db\00a0\00a0\21da";
}
.CodeMirror-merge-scrolllock.CodeMirror-merge-scrolllock-enabled:after {
content: "\21db\21da";
}
.CodeMirror-merge-copybuttons-left, .CodeMirror-merge-copybuttons-right {
position: absolute;
left: 0; top: 0;
right: 0; bottom: 0;
line-height: 1;
}
.CodeMirror-merge-copy {
position: absolute;
cursor: pointer;
color: #44c;
z-index: 3;
}
.CodeMirror-merge-copy-reverse {
position: absolute;
cursor: pointer;
color: #44c;
}
.CodeMirror-merge-copybuttons-left .CodeMirror-merge-copy { left: 2px; }
.CodeMirror-merge-copybuttons-right .CodeMirror-merge-copy { right: 2px; }
.CodeMirror-merge-r-inserted, .CodeMirror-merge-l-inserted {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAACCAYAAACddGYaAAAAGUlEQVQI12MwuCXy3+CWyH8GBgYGJgYkAABZbAQ9ELXurwAAAABJRU5ErkJggg==);
background-position: bottom left;
background-repeat: repeat-x;
}
.CodeMirror-merge-r-deleted, .CodeMirror-merge-l-deleted {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAACCAYAAACddGYaAAAAGUlEQVQI12M4Kyb2/6yY2H8GBgYGJgYkAABURgPz6Ks7wQAAAABJRU5ErkJggg==);
background-position: bottom left;
background-repeat: repeat-x;
}
.CodeMirror-merge-r-chunk { background: #ffffe0; }
.CodeMirror-merge-r-chunk-start { border-top: 1px solid #ee8; }
.CodeMirror-merge-r-chunk-end { border-bottom: 1px solid #ee8; }
.CodeMirror-merge-r-connect { fill: #ffffe0; stroke: #ee8; stroke-width: 1px; }
.CodeMirror-merge-l-chunk { background: #eef; }
.CodeMirror-merge-l-chunk-start { border-top: 1px solid #88e; }
.CodeMirror-merge-l-chunk-end { border-bottom: 1px solid #88e; }
.CodeMirror-merge-l-connect { fill: #eef; stroke: #88e; stroke-width: 1px; }
.CodeMirror-merge-l-chunk.CodeMirror-merge-r-chunk { background: #dfd; }
.CodeMirror-merge-l-chunk-start.CodeMirror-merge-r-chunk-start { border-top: 1px solid #4e4; }
.CodeMirror-merge-l-chunk-end.CodeMirror-merge-r-chunk-end { border-bottom: 1px solid #4e4; }
.CodeMirror-merge-collapsed-widget:before {
content: "(...)";
}
.CodeMirror-merge-collapsed-widget {
cursor: pointer;
color: #88b;
background: #eef;
border: 1px solid #ddf;
font-size: 90%;
padding: 0 3px;
border-radius: 4px;
}
.CodeMirror-merge-collapsed-line .CodeMirror-gutter-elt { display: none; }

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,64 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"), "cjs");
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], function(CM) { mod(CM, "amd"); });
else // Plain browser env
mod(CodeMirror, "plain");
})(function(CodeMirror, env) {
if (!CodeMirror.modeURL) CodeMirror.modeURL = "../mode/%N/%N.js";
var loading = {};
function splitCallback(cont, n) {
var countDown = n;
return function() { if (--countDown == 0) cont(); };
}
function ensureDeps(mode, cont) {
var deps = CodeMirror.modes[mode].dependencies;
if (!deps) return cont();
var missing = [];
for (var i = 0; i < deps.length; ++i) {
if (!CodeMirror.modes.hasOwnProperty(deps[i]))
missing.push(deps[i]);
}
if (!missing.length) return cont();
var split = splitCallback(cont, missing.length);
for (var i = 0; i < missing.length; ++i)
CodeMirror.requireMode(missing[i], split);
}
CodeMirror.requireMode = function(mode, cont) {
if (typeof mode != "string") mode = mode.name;
if (CodeMirror.modes.hasOwnProperty(mode)) return ensureDeps(mode, cont);
if (loading.hasOwnProperty(mode)) return loading[mode].push(cont);
var file = CodeMirror.modeURL.replace(/%N/g, mode);
if (env == "plain") {
var script = document.createElement("script");
script.src = file;
var others = document.getElementsByTagName("script")[0];
var list = loading[mode] = [cont];
CodeMirror.on(script, "load", function() {
ensureDeps(mode, function() {
for (var i = 0; i < list.length; ++i) list[i]();
});
});
others.parentNode.insertBefore(script, others);
} else if (env == "cjs") {
require(file);
cont();
} else if (env == "amd") {
requirejs([file], cont);
}
};
CodeMirror.autoLoadMode = function(instance, mode) {
if (!CodeMirror.modes.hasOwnProperty(mode))
CodeMirror.requireMode(mode, function() {
instance.setOption("mode", instance.getOption("mode"));
});
};
});

Some files were not shown because too many files have changed in this diff Show More