diff --git a/.gitignore b/.gitignore
index 9273f9f2..ed56077a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,5 @@
-tmp/
var/
pkg/
-bin/
*.swp
# Binaries for programs and plugins
*.exe
diff --git a/etc/exit.shy b/etc/exit.shy
index a49b1b48..917cd278 100644
--- a/etc/exit.shy
+++ b/etc/exit.shy
@@ -1,6 +1,6 @@
source etc/local_exit.shy
~file1
- history save etc/history.txt
+ history save var/history.txt
~aaa
- login save etc/login.txt
+ login save var/login.txt
diff --git a/etc/init.shy b/etc/init.shy
index 3844103a..585dd826 100644
--- a/etc/init.shy
+++ b/etc/init.shy
@@ -1,8 +1,8 @@
login root root
~aaa
- login load etc/login.txt
+ login load var/login.txt
~file1
- history load etc/history.txt
+ history load var/history.txt
source etc/local.shy
diff --git a/etc/cert.pem b/etc/pem/cert.pem
similarity index 100%
rename from etc/cert.pem
rename to etc/pem/cert.pem
diff --git a/etc/key.pem b/etc/pem/key.pem
similarity index 100%
rename from etc/key.pem
rename to etc/pem/key.pem
diff --git a/src/contexts/aaa/aaa.go b/src/contexts/aaa/aaa.go
index c550576d..183c1e39 100644
--- a/src/contexts/aaa/aaa.go
+++ b/src/contexts/aaa/aaa.go
@@ -2,7 +2,12 @@ package aaa // {{{
// }}}
import ( // {{{
"contexts"
+ "crypto/sha1"
"math/big"
+ "net/http"
+ "sort"
+
+ "encoding/json"
"bufio"
"io"
@@ -147,8 +152,38 @@ var Pulse *ctx.Message
var Index = &ctx.Context{Name: "aaa", Help: "认证中心",
Caches: map[string]*ctx.Cache{
"nuser": &ctx.Cache{Name: "nuser", Value: "0", Help: "用户数量"},
+
+ "access_expire": &ctx.Cache{Name: "会话超时", Value: "0", Help: "会话超时"},
+ "access_token": &ctx.Cache{Name: "会话令牌", Value: "0", Help: "会话令牌", Hand: func(m *ctx.Message, x *ctx.Cache, arg ...string) string {
+ if len(arg) > 0 { // {{{
+ return arg[0]
+ }
+
+ if m.Capi("access_expire") < int(time.Now().Unix()) {
+ var data struct {
+ Errcode int64
+ Errmsg string
+ Access_token string
+ Expires_in int64
+ }
+ res, e := http.Get(fmt.Sprintf(m.Conf("wx_api")+m.Conf("access_route"), m.Conf("appid"), m.Conf("appmm")))
+ m.Assert(e)
+ m.Assert(json.NewDecoder(res.Body).Decode(&data))
+ m.Cap("access_expire", fmt.Sprintf("%d", time.Now().Unix()+data.Expires_in))
+ x.Value = data.Access_token
+ m.Log("info", "access_token: %s(%s)", m.Cap("access_token"), m.Cap("access_expire"))
+ m.Cap("stream", m.Conf("appid"))
+ }
+ return x.Value // }}}
+ }},
},
Configs: map[string]*ctx.Config{
+ "wx_api": &ctx.Config{Name: "微信接口", Value: "https://api.weixin.qq.com/cgi-bin/", Help: "微信登录"},
+ "access_route": &ctx.Config{Name: "微信登录", Value: "token?grant_type=client_credential&appid=%s&secret=%s", Help: "微信登录"},
+ "appid": &ctx.Config{Name: "微信帐号", Value: "", Help: "微信帐号"},
+ "appmm": &ctx.Config{Name: "微信密码", Value: "", Help: "微信密码"},
+ "token": &ctx.Config{Name: "微信令牌", Value: "", Help: "微信密码"},
+
"rootname": &ctx.Config{Name: "rootname", Value: "root", Help: "根用户名"},
"expire": &ctx.Config{Name: "expire(s)", Value: "7200", Help: "会话超时"},
"pub": &ctx.Config{Name: "pub", Value: "etc/pub.pem", Help: "公钥文件"},
@@ -171,9 +206,24 @@ var Index = &ctx.Context{Name: "aaa", Help: "认证中心",
Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) {
if aaa, ok := c.Server.(*AAA); m.Assert(ok) { // {{{
stream := ""
+ method := ""
+ username := ""
+ m.Log("fuck", "%s %s", method, username)
+ if len(arg) > 0 {
+ switch arg[0] {
+ case "openid":
+ method = arg[0]
+ username = arg[1]
+ stream = arg[1]
+ }
+ }
+
if m.Has("ip") {
stream = m.Option("ip")
}
+ if m.Has("openid") {
+ stream = m.Option("openid")
+ }
if m.Has("pub") {
stream = m.Option("pub")
buf, e := ioutil.ReadFile(m.Option("pub"))
@@ -249,7 +299,7 @@ var Index = &ctx.Context{Name: "aaa", Help: "认证中心",
word := strings.SplitN(bio.Text(), ":", 3)
msg := m.Spawn()
msg.Start(word[0], "用户", word[0], word[1], word[2])
- msg.Spawn().Cmd("config", "load", fmt.Sprintf("etc/%s.json", word[0]), "lark")
+ msg.Spawn().Cmd("config", "load", fmt.Sprintf("var/%s.json", word[0]), "lark")
}
}
case "save":
@@ -257,7 +307,7 @@ var Index = &ctx.Context{Name: "aaa", Help: "认证中心",
m.Travel(func(m *ctx.Message, i int) bool {
if i > 0 && m.Cap("username") != "root" {
f.WriteString(fmt.Sprintf("%s:%s:%s\n", m.Cap("username"), m.Cap("password"), m.Cap("sessid")))
- m.Spawn().Cmd("config", "save", fmt.Sprintf("etc/%s.json", m.Cap("username")), "lark")
+ m.Spawn().Cmd("config", "save", fmt.Sprintf("var/%s.json", m.Cap("username")), "lark")
}
return true
})
@@ -701,6 +751,15 @@ var Index = &ctx.Context{Name: "aaa", Help: "认证中心",
}
// }}}
}},
+ "wx": &ctx.Command{Name: "wx check signature", Help: "微信", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) {
+ list := []string{m.Option("nonce"), m.Option("timestamp"), m.Conf("token")}
+ sort.Strings(list)
+ b := sha1.Sum([]byte(strings.Join(list, "")))
+
+ if m.Option("signature") == hex.EncodeToString(b[:]) {
+ m.Echo("ok")
+ }
+ }},
},
}
diff --git a/src/contexts/ctx.go b/src/contexts/ctx.go
index c99ca7d8..3b8afeb3 100644
--- a/src/contexts/ctx.go
+++ b/src/contexts/ctx.go
@@ -3466,6 +3466,7 @@ func Start(args ...string) {
Pulse.Sess("cli", "cli")
Pulse.Sess("aaa", "aaa")
+ Pulse.Sess("web", "web")
Pulse.Sess("log", "log")
if len(args) > 0 {
diff --git a/src/contexts/web/web.go b/src/contexts/web/web.go
index d0180979..975315ad 100644
--- a/src/contexts/web/web.go
+++ b/src/contexts/web/web.go
@@ -8,6 +8,7 @@ import ( // {{{
"runtime"
"encoding/json"
+ "encoding/xml"
"html/template"
"io/ioutil"
"net/http"
@@ -106,6 +107,8 @@ func (web *WEB) Trans(m *ctx.Message, key string, hand func(*ctx.Message, *ctx.C
hand(msg, msg.Target(), msg.Option("path"))
switch {
+ case msg.Has("directory"):
+ http.ServeFile(w, r, msg.Append("directory"))
case msg.Has("redirect"):
http.Redirect(w, r, msg.Append("redirect"), http.StatusFound)
case msg.Has("template"):
@@ -139,7 +142,7 @@ func (web *WEB) ServeHTTP(w http.ResponseWriter, r *http.Request) { // {{{
}
if r.URL.Path == "/" && m.Confs("root_index") {
- http.Redirect(w, r, "/index/", http.StatusFound)
+ http.Redirect(w, r, m.Conf("root_index"), http.StatusFound)
} else {
web.ServeMux.ServeHTTP(w, r)
}
@@ -254,6 +257,7 @@ func (web *WEB) Start(m *ctx.Message, arg ...string) bool { // {{{
yac.Cmd("train", "code", "struct", "typedef", "struct", "key", "key", ";")
yac.Cmd("train", "code", "function", "key", "\\*", "key", "(", "other")
yac.Cmd("train", "code", "function", "key", "key", "(", "other")
+ yac.Cmd("train", "code", "variable", "struct", "key", "key", "other")
yac.Cmd("train", "code", "define", "#define", "key", "other")
if m.Cap("protocol") == "https" {
@@ -287,20 +291,23 @@ var Index = &ctx.Context{Name: "web", Help: "应用中心",
"nroute": &ctx.Cache{Name: "nroute", Value: "0", Help: "路由数量"},
},
Configs: map[string]*ctx.Config{
- "cmd": &ctx.Config{Name: "cmd", Value: "tmux", Help: "路由数量"},
- "cert": &ctx.Config{Name: "cert", Value: "etc/cert.pem", Help: "路由数量"},
- "key": &ctx.Config{Name: "key", Value: "etc/key.pem", Help: "路由数量"},
- "wiki_dir": &ctx.Config{Name: "wiki_dir", Value: "usr/wiki", Help: "路由数量"},
+ "cmd": &ctx.Config{Name: "cmd", Value: "tmux", Help: "路由数量"},
+ "cert": &ctx.Config{Name: "cert", Value: "etc/cert.pem", Help: "路由数量"},
+ "key": &ctx.Config{Name: "key", Value: "etc/key.pem", Help: "路由数量"},
+ "wiki_dir": &ctx.Config{Name: "wiki_dir", Value: "usr/wiki", Help: "路由数量"},
+ "wiki_list_show": &ctx.Config{Name: "wiki_list_show", Value: map[string]interface{}{
+ "md": true,
+ }, Help: "路由数量"},
"which": &ctx.Config{Name: "which", Value: "redis.note", Help: "路由数量"},
- "root_index": &ctx.Config{Name: "root_index(true/false)", Value: "true", Help: "路由数量"},
+ "root_index": &ctx.Config{Name: "root_index", Value: "/wiki/", Help: "路由数量"},
"auto_create": &ctx.Config{Name: "auto_create(true/false)", Value: "true", Help: "路由数量"},
"refresh_time": &ctx.Config{Name: "refresh_time(ms)", Value: "1000", Help: "路由数量"},
"define": &ctx.Config{Name: "define", Value: map[string]interface{}{
"ngx_command_t": map[string]interface{}{
- "position": map[string]interface{}{
+ "position": []interface{}{map[string]interface{}{
"file": "nginx-1.15.2/src/core/ngx_core.h",
"line": "22",
- },
+ }},
},
"ngx_command_s": map[string]interface{}{
"position": map[string]interface{}{
@@ -562,6 +569,11 @@ var Index = &ctx.Context{Name: "web", Help: "应用中心",
m.Assert(e)
defer file.Close()
+ if m.Option("type") == "json" {
+ contenttype = "application/json"
+ body = file
+ break
+ }
buf := &bytes.Buffer{}
writer := multipart.NewWriter(buf)
@@ -1193,7 +1205,6 @@ var Index = &ctx.Context{Name: "web", Help: "应用中心",
case "pyc", "o", "gz", "tar":
continue
case "c":
- m.Log("fuck", "parse %s", name)
case "h":
default:
continue
@@ -1202,8 +1213,7 @@ var Index = &ctx.Context{Name: "web", Help: "应用中心",
f, e := os.Open(name)
m.Assert(e)
defer f.Close()
-
- m.Log("fuck", "parse %d/%d %s", i, len(msg.Meta["filename"]), name)
+ m.Log("fuck", "%d/%d %s", i, len(msg.Meta["filename"]), v)
bio := bufio.NewScanner(f)
for line := 1; bio.Scan(); line++ {
@@ -1228,11 +1238,16 @@ var Index = &ctx.Context{Name: "web", Help: "应用中心",
default:
key = l.Result(3)
}
+ case "variable":
+ switch l.Result(2) {
+ case "struct":
+ key = l.Result(4)
+ }
case "define":
key = l.Result(3)
}
if key != "" {
- m.Confv("define", strings.Join([]string{key, "position"}, "."), map[string]interface{}{
+ m.Confv("define", strings.Join([]string{key, "position", "-2"}, "."), map[string]interface{}{
"file": strings.TrimPrefix(name, m.Confx("wiki_dir")),
"line": line,
"type": l.Result(1),
@@ -1271,10 +1286,11 @@ var Index = &ctx.Context{Name: "web", Help: "应用中心",
if m.Options("query") {
if v, ok := m.Confv("define", m.Option("query")).(map[string]interface{}); ok {
- if val, ok := v["position"].(map[string]interface{}); ok {
- m.Add("append", "name", fmt.Sprintf("%v#hash_%v", val["file"], val["line"]))
- return
+ for _, val := range v["position"].([]interface{}) {
+ value := val.(map[string]interface{})
+ m.Add("append", "name", fmt.Sprintf("%v#hash_%v", value["file"], value["line"]))
}
+ return
}
msg := m.Sess("nfs").Cmd("dir", path.Join(m.Conf("wiki_dir"), m.Option("dir")), "dir_name", "path")
for _, v := range msg.Meta["filename"] {
@@ -1309,21 +1325,155 @@ var Index = &ctx.Context{Name: "web", Help: "应用中心",
if l.Name()[0] == '.' {
continue
}
- es := strings.Split(l.Name(), ".")
- if len(es) > 0 {
- switch es[len(es)-1] {
- case "pyc", "o", "gz", "tar":
- continue
+ if !l.IsDir() {
+ es := strings.Split(l.Name(), ".")
+ if len(es) > 0 {
+ if show, ok := m.Confv("wiki_list_show", es[len(es)-1]).(bool); !ok || !show {
+ continue
+ }
}
}
m.Add("append", "name", l.Name())
+ m.Add("append", "time", l.ModTime().Format("2006-01-02 15:04:05"))
+ if l.IsDir() {
+ m.Add("append", "pend", "/")
+ } else {
+ m.Add("append", "pend", "")
+ }
+ m.Option("time_layout", "2006-01-02 15:04:05")
+ m.Sort("time", "time_r")
}
}},
"/wiki/": &ctx.Command{Name: "/wiki", Help: "维基", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) {
m.Option("which", strings.TrimPrefix(key, "/wiki/"))
+ if f, e := os.Stat(path.Join(m.Conf("wiki_dir"), m.Option("which"))); e == nil && !f.IsDir() && (strings.HasSuffix(m.Option("which"), ".json") || strings.HasSuffix(m.Option("which"), ".js") || strings.HasSuffix(m.Option("which"), ".css")) {
+ m.Append("directory", path.Join(m.Conf("wiki_dir"), m.Option("which")))
+ return
+ }
+
m.Append("template", "wiki")
}},
+ "/wx/": &ctx.Command{Name: "/wx/", Help: "微信", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) {
+ if !m.Sess("aaa").Cmd("wx").Results(0) {
+ return
+ }
+ if m.Has("echostr") {
+ m.Echo(m.Option("echostr"))
+ return
+ }
+ r := m.Optionv("request").(*http.Request)
+
+ switch r.Header.Get("Content-Type") {
+ case "text/xml":
+ type Article struct {
+ XMLName xml.Name `xml:"item"`
+ PicUrl string
+ Title string
+ Description string
+ Url string
+ }
+ type WXMsg struct {
+ XMLName xml.Name `xml:"xml"`
+ ToUserName string
+ FromUserName string
+ CreateTime int32
+ MsgId int64
+ MsgType string
+
+ Event string
+ EventKey string
+
+ Content string
+
+ Format string
+ Recognition string
+
+ PicUrl string
+ MediaId string
+
+ Location_X float64
+ Location_Y float64
+ Scale int64
+ Label string
+
+ ArticleCount int
+ Articles struct {
+ XMLName xml.Name `xml:"Articles"`
+ Articles []*Article
+ }
+ }
+
+ var data WXMsg
+
+ b, e := ioutil.ReadAll(r.Body)
+ m.Log("fuck", "b: %v", string(b))
+ e = xml.Unmarshal(b, &data)
+ m.Log("fuck", "b: %#v", data)
+
+ // de := xml.NewDecoder(r.Body)
+ // e := de.Decode(&data)
+ m.Assert(e)
+
+ var echo WXMsg
+ echo.FromUserName = data.ToUserName
+ echo.ToUserName = data.FromUserName
+ echo.CreateTime = data.CreateTime
+
+ fs, e := ioutil.ReadDir("usr/wiki")
+ m.Assert(e)
+ msg := m.Spawn()
+ for _, f := range fs {
+ if !strings.HasSuffix(f.Name(), ".md") {
+ continue
+ }
+ msg.Add("append", "name", f.Name())
+ msg.Add("append", "title", strings.TrimSuffix(f.Name(), ".md")+"源码解析")
+ msg.Add("append", "time", f.ModTime().Format("01/02 15:03"))
+ }
+ msg.Option("time_layout", "01/02 15:03")
+ msg.Sort("time", "time_r")
+
+ articles := []*Article{}
+ articles = append(articles, &Article{PicUrl: "http://mmbiz.qpic.cn/mmbiz_jpg/sCJZHmp0V0doWEFBe6gS2HjgB0abiaK7H5WjkXGTvAI0CkCFrVJDEBBbJX8Kz0VegZ54ZoCo4We0sKJUOTuf1Tw/0",
+ Title: "wiki首页", Description: "技术文章", Url: "https://shylinux.com/wiki/"})
+ for i, v := range msg.Meta["title"] {
+ if i > 6 {
+ continue
+ }
+
+ articles = append(articles, &Article{PicUrl: "http://mmbiz.qpic.cn/mmbiz_jpg/sCJZHmp0V0doWEFBe6gS2HjgB0abiaK7H5WjkXGTvAI0CkCFrVJDEBBbJX8Kz0VegZ54ZoCo4We0sKJUOTuf1Tw/0",
+ Title: msg.Meta["time"][i] + " " + v, Description: "技术文章", Url: "https://shylinux.com/wiki/" + msg.Meta["name"][i]})
+ }
+
+ switch data.MsgType {
+ case "event":
+ echo.MsgType = "news"
+ echo.Articles.Articles = articles
+ echo.ArticleCount = len(echo.Articles.Articles)
+ case "text":
+ echo.MsgType = "news"
+ echo.Articles.Articles = articles
+ echo.ArticleCount = len(echo.Articles.Articles)
+ case "voice":
+ echo.MsgType = "text"
+ echo.Content = "你好"
+ case "image":
+ echo.MsgType = "text"
+ echo.Content = "你好"
+ case "location":
+ echo.MsgType = "text"
+ echo.Content = "你好"
+ }
+
+ b, e = xml.Marshal(echo)
+ m.Echo(string(b))
+ }
+ }},
+ "user": &ctx.Command{Name: "user", Help: "应用示例", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) {
+ aaa := m.Sess("aaa")
+ m.Spawn().Cmd("get", fmt.Sprintf("%suser/get", aaa.Conf("wx_api")), "access_token", aaa.Cap("access_token"))
+ }},
"temp": &ctx.Command{Name: "temp", Help: "应用示例", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) {
msg := m.Spawn(m.Target())
question := []string{}
diff --git a/usr/library/context.js b/usr/library/context.js
index 085ef767..d53f55e8 100644
--- a/usr/library/context.js
+++ b/usr/library/context.js
@@ -41,6 +41,52 @@ ctx = {
}
location.search = arg.join("&");
},//}}}
+ GET: function(url, form, cb) {//{{{
+ var xhr = new XMLHttpRequest();
+ xhr.onreadystatechange = function() {
+ switch (xhr.readyState) {
+ case 4:
+ switch (xhr.status) {
+ case 200:
+ try {
+ var msg = JSON.parse(xhr.responseText||'{"result":[]}');
+ } catch (e) {
+ msg = {"result": [xhr.responseText]}
+ }
+
+ msg && console.log(msg)
+ msg.result && console.log(msg.result.join(""));
+ typeof cb == "function" && cb(msg)
+ }
+ break;
+ }
+ }
+
+ form = form || {}
+ form["dir"] = form["dir"] || this.Search("dir") || undefined
+ form["module"] = form["module"] || this.Search("module") || undefined
+ form["domain"] = form["domain"] || this.Search("domain") || undefined
+
+ var args = [];
+ for (k in form) {
+ if (form[k] instanceof Array) {
+ for (i in form[k]) {
+ args.push(k+"="+encodeURIComponent(form[k][i]));
+ }
+ } else if (form[k] != undefined) {
+ args.push(k+"="+encodeURIComponent(form[k]));
+ }
+ }
+
+ var arg = args.join("&");
+ if (arg) {
+ url += "?"+arg
+ }
+
+ xhr.open("GET", url);
+ console.log("GET: "+url+"?"+arg);
+ xhr.send();
+ },//}}}
POST: function(url, form, cb) {//{{{
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
diff --git a/usr/template/common/base.html b/usr/template/common/base.html
index c95defb3..26466984 100644
--- a/usr/template/common/base.html
+++ b/usr/template/common/base.html
@@ -1,7 +1,7 @@
{{define "head"}}
-
+
{{option .Meta "page_title"}}
{{range option . "nline"|meta|list}}
-
{{.}}
+
{{.}}
{{end}}
{{$msg := .}}
{{if append . "name"}}
-
- {{range append . "name"}}
- -
-
-
- {{end}}
-
+ {{$l := append . "name"|len}}
+ {{if eq $l 1}}
+
+ {{else}}
+
{{append . "code"|meta}}
+
{{append . "code"|meta}}
{{end}}
+
{{end}}
diff --git a/usr/wiki/html5.css b/usr/wiki/html5.css
new file mode 100644
index 00000000..9e169c8e
--- /dev/null
+++ b/usr/wiki/html5.css
@@ -0,0 +1,8 @@
+canvas {
+ border: solid 1px green;
+}
+.demo {
+ float:left;
+ margin-right:10px;
+ border: solid 1px green;
+}
diff --git a/usr/wiki/html5.js b/usr/wiki/html5.js
new file mode 100644
index 00000000..2b2790bd
--- /dev/null
+++ b/usr/wiki/html5.js
@@ -0,0 +1,519 @@
+var canvas = document.getElementById("heart");
+var ctx = canvas.getContext('2d');
+
+var main_angle = 30;
+function refreshHeart() {
+ ctx.clearRect(0,0,400,400)
+ drawHeart(ctx,200,200,60,main_angle);
+ main_angle += 10;
+ for (var i = 0; i < 10; i++) {
+ var x = Math.random() * 400;
+ var y = Math.random() * 400;
+ var scale = Math.random() * 20+10;
+ var angle = Math.random() * 360;
+ drawHeart(ctx,x,y,scale,angle);
+ }
+ setTimeout(refreshHeart, 200);
+}
+setTimeout(refreshHeart, 200);
+
+function drawHeart(ctx,x,y,scale,angle, style, stroke) {//{{{
+ ctx.save();
+ ctx.translate(x,y);
+ ctx.rotate(angle/180*Math.PI);
+ ctx.scale(scale, scale);
+ heartPath(ctx);
+ ctx.shadowColor = "gray";
+ ctx.shadowOffsetX = 5;
+ ctx.shadowOffsetY = 5;
+ ctx.shadowBlur = 5;
+ if (stroke == "stroke") {
+ ctx.strokeStyle = style||"red";
+ ctx.stroke();
+ } else {
+ ctx.fillStyle = style||"red";
+ ctx.fill();
+ }
+ ctx.restore();
+}
+//}}}
+function heartPath(ctx) {//{{{
+ ctx.beginPath();
+ ctx.arc(-1,0,1,Math.PI,0,false);
+ ctx.arc(1,0,1,Math.PI,0,false);
+ ctx.bezierCurveTo(1.9, 1.2, 0.6, 1.6, 0, 3.0);
+ ctx.bezierCurveTo( -0.6, 1.6,-1.9, 1.2,-2,0);
+ ctx.closePath();
+}
+//}}}
+
+var ctx0 = document.getElementById("demo0").getContext("2d");
+ctx0.fillStyle = "green";
+ctx0.fillRect(10,10,100,100);
+
+var ctx2 = document.getElementById("demo2").getContext("2d");
+ctx2.beginPath();
+ctx2.moveTo(60,10);
+ctx2.lineTo(10,110);
+ctx2.lineTo(110,110);
+ctx2.fill();
+
+
+function draw3() {
+ for (var i = 0; i < 120; i+=20) {
+ for (var j = 0; j < 120; j+=20) {
+ r = Math.random()*255;
+ g = Math.random()*255;
+ b = Math.random()*255;
+ ctx3.fillStyle = "rgb("+r+","+g+","+b+")";
+ ctx3.fillRect(i, j, 20, 20);
+ }
+ }
+}
+var demo3 = document.getElementById("demo3");
+var ctx3 = demo3.getContext("2d");
+demo3.onclick = draw3;
+draw3()
+
+var select_pan = document.getElementById("select_pan");
+var his = document.getElementById("draw_history");
+var show = document.getElementById("show");
+var item = document.getElementById("draw");
+var draw = item.getContext("2d");
+
+var control_map = {//{{{
+ s: "stroke",
+ f: "fill",
+
+ e: "heart",
+ c: "cycle",
+ r: "rect",
+ v: "line",
+ t: "text",
+
+ d: "delete",
+
+ b: "big",
+ m: "small",
+ a: "play",
+
+ Escape: "escape",
+}
+//}}}
+function control(event) {//{{{
+ if (event.type == "keyup") {
+ action(event, control_map[event.key]);
+ }
+}
+//}}}
+
+var current_ctx = {//{{{
+ hide: 0,
+ scale: 1,
+ index_point: false,
+ shape: 'cycle',
+ stroke: 'stroke',
+ color: 'red',
+ font: '32px sans-serif',
+ big_scale: 1.25,
+ small_scale: 0.8,
+ begin_point: null,
+ end_point: null,
+ list: {
+ style: ["red", "green", "yellow", "blue"],
+ stroke: ["fill", "stroke"],
+ shape: ["heart", "cycle", "rect", "line"],
+ }
+}
+//}}}
+function display(which, group) {//{{{
+ var cs = document.getElementsByClassName(group);
+ for (var i = 0; i < cs.length; i++) {
+ cs[i].style.backgroundColor = "white";
+ }
+
+ var cs = document.getElementsByClassName(group+" "+which);
+ for (var i = 0; i < cs.length; i++) {
+ cs[i].style.backgroundColor = "lightblue";
+ }
+}
+//}}}
+function action(event, s) {//{{{
+ console.log(event);
+ switch (s) {
+ case "escape":
+ current_ctx.begin_point = null;
+ current_ctx.end_point = null;
+ refresh();
+ break
+ case "big":
+ current_ctx.scale *= current_ctx.big_scale;
+ draw.scale(current_ctx.big_scale, current_ctx.big_scale);
+ refresh();
+ break
+ case "small":
+ current_ctx.scale *= current_ctx.small_scale;
+ draw.scale(current_ctx.small_scale, current_ctx.small_scale);
+ refresh();
+ break
+ case "clear":
+ if (confirm("clear all?")) {
+ action(event, "clear");
+ his.innerHTML = "";
+ draw_history = [];
+ refresh();
+ }
+ break
+ case "hide":
+ current_ctx.hide = draw_history.length;
+ refresh();
+ break
+ case "delete":
+ draw_history.pop();
+ refresh();
+ break
+ case "draw":
+ current_ctx.hide = 0;
+ refresh();
+ break
+
+ case "fill":
+ current_ctx.stroke = "fill";
+ select_pan.selectedIndex=1;
+ break
+ case "stroke":
+ current_ctx.stroke = "stroke";
+ select_pan.selectedIndex=0;
+ break
+
+ case "heart":
+ current_ctx.shape = s;
+ current_ctx.stroke = "fill";
+ display("e", "control");
+ select_pan.selectedIndex=1;
+ break
+ case "cycle":
+ current_ctx.shape = s;
+ display("c", "control");
+ break
+ case "rect":
+ current_ctx.shape = s;
+ display("r", "control");
+ break
+ case "line":
+ current_ctx.shape = s;
+ current_ctx.stroke = "stroke";
+ select_pan.selectedIndex=0;
+ display("v", "control");
+ break
+ case "text":
+ current_ctx.shape = s;
+ current_ctx.stroke = "fill";
+ select_pan.selectedIndex=0;
+ display("t", "control");
+ break
+
+ case "play":
+ current_ctx.hide = 0;
+ refresh(500, 0);
+ break
+ }
+}
+//}}}
+function select(event, config, val) {//{{{
+ var target = event.target;
+ var value = target[target.selectedIndex].value;
+ switch (config) {
+ case "color":
+ current_ctx.color = value;
+ break
+ case "stroke":
+ current_ctx.stroke = value;
+ break
+ case "shape":
+ display("r", "control");
+ switch (value) {
+ case "heart":
+ current_ctx.stroke = "fill";
+ display("h", "control");
+ break
+ case "cycle":
+ display("c", "control");
+ break
+ case "rect":
+ display("r", "control");
+ break
+ case "line":
+ current_ctx.stroke = "stroke";
+ display("v", "control");
+ break
+ }
+ current_ctx.shape = value;
+ break
+ }
+}
+//}}}
+
+var draw_history = [];
+function modify(event, row, col) {//{{{
+ if (event.key != "Enter") {
+ return
+ }
+ console.log("modify");
+ console.log(event);
+ console.log();
+ var data = event.target.dataset;
+ var row = draw_history[data.row];
+ var value = event.target.value;
+ switch (data.col) {
+ case "x1":
+ row.begin_point.x = value;
+ break
+ case "y1":
+ row.begin_point.y = value;
+ break
+ case "x2":
+ row.end_point.x = value;
+ break
+ case "y2":
+ row.end_point.y = value;
+ break
+ default:
+ row[data.col] = value;
+ }
+
+ refresh();
+}
+//}}}
+function refresh(time, i) {//{{{
+ if (time) {
+ if (0 == i) {
+ draw.clearRect(0, 0,400/current_ctx.scale, 400/current_ctx.scale);
+ }
+
+ if (current_ctx.hide <= i && i < draw_history.length) {
+ var h = draw_history[i];
+ draws(draw, h.style, h.stroke, h.shape, h.begin_point, h.end_point);
+ i++;
+ setTimeout(function(){
+ refresh(time, i);
+ }, time);
+ }
+ return
+ }
+
+ draw.clearRect(0, 0,400/current_ctx.scale, 400/current_ctx.scale);
+
+ for (var i in draw_history) {
+ if (i
+
+## 简介
+
+- 文档:
+- 文档:
+
+### miniCAD在线绘图
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+### canvas绘图
+
+```
+
+
+```
+
+### 画矩形
+
+```
+fillRect(x, y, width, height)
+strokeRect(x, y, width, height)
+clearRect(x, y, width, height)
+```
+
+### 画路径
+
+```
+
+
+```
+
+```
+beginPath()
+moveTo(x, y)
+LineTo(x, y)
+closePath()
+stroke()
+fill()
+
+arc(x, y, radius, startAngle, endAngle, anticlockwise)
+arcTo(x1, y1, x2, y2, radius)
+quadraticCurveTo(cp1x, cp1y, x, y)
+bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)
+new Path2D()
+```
+### 设置样式
+
+```
+fillStyle = "red"
+fillStyle = "#FF0000"
+fillStyle = "rgb(255,0,0)"
+fillStyle = "rgb(255,0,0,1)"
+strokeStyle =
+
+var img = new Image();
+img.src = "img.png";
+img.onLoad = function() {}
+createPattern(img, style)
+
+createLinearGradient(x1, y1, x2, y2)
+createRadialGradient(x1, y1, r1, x2, y2, r2)
+ addColorStop(position, color)
+
+shadowOffsetX
+shadowOffsetY
+shadowBlur
+shadowColor
+
+lineWidth
+lineCap
+lineJoin
+```
+### 输出文字
+```
+font
+textAlign
+textBaseline
+direction
+measureText()
+fillText(text, x, y[, maxWidth])
+strokeText(text, x, y[, maxWidth])
+```
+
+### 坐标变换
+```
+save()
+restore()
+translate(x,y)
+rotate(angle)
+scale(x, y)
+transform(a,b,c,d,e,f)
+setTransform(a,b,c,d,e,f)
+resetTransform()
+```
+
+
+