1
0
mirror of https://shylinux.com/x/ContextOS synced 2025-04-26 17:24:05 +08:00

opt ssh.componet

This commit is contained in:
shaoying 2019-08-31 00:27:49 +08:00
parent 0cefdaa032
commit 307c15fad2
8 changed files with 87 additions and 144 deletions

View File

@ -121,10 +121,10 @@ var Index = &ctx.Context{Name: "cli", Help: "管理中心",
}, Help: "源码编译"}, }, Help: "源码编译"},
"publish": &ctx.Config{Name: "publish", Value: map[string]interface{}{ "publish": &ctx.Config{Name: "publish", Value: map[string]interface{}{
"path": "usr/publish", "list": map[string]interface{}{ "path": "usr/publish", "list": map[string]interface{}{
"boot_sh": "bin/boot.sh", "boot_sh": "bin/boot.sh",
"zone_sh": "bin/zone.sh", "zone_sh": "bin/zone.sh",
"user_sh": "bin/user.sh", "user_sh": "bin/user.sh",
"node_sh": "bin/node.sh", "node_sh": "bin/node.sh",
"init_shy": "etc/init.shy", "init_shy": "etc/init.shy",
"common_shy": "etc/common.shy", "common_shy": "etc/common.shy",
@ -145,10 +145,10 @@ var Index = &ctx.Context{Name: "cli", Help: "管理中心",
"list": map[string]interface{}{ "list": map[string]interface{}{
"bench": "bin/bench.new", "bench": "bin/bench.new",
"boot_sh": "bin/boot.sh", "boot_sh": "bin/boot.sh",
"zone_sh": "bin/zone.sh", "zone_sh": "bin/zone.sh",
"user_sh": "bin/user.sh", "user_sh": "bin/user.sh",
"node_sh": "bin/node.sh", "node_sh": "bin/node.sh",
"init_shy": "etc/init.shy", "init_shy": "etc/init.shy",
"common_shy": "etc/common.shy", "common_shy": "etc/common.shy",
@ -248,7 +248,7 @@ var Index = &ctx.Context{Name: "cli", Help: "管理中心",
"cmd_temp": -1, "cmd_temp": -1,
"cmd_parse": 2, "cmd_parse": 2,
"cmd_error": 0, "cmd_error": 0,
"cmd_select": -1, "cmd_select": -1,
"app_log": 1, "app_log": 1,
}, Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) (e error) { }, Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) (e error) {
// 管道参数 // 管道参数
@ -706,8 +706,8 @@ var Index = &ctx.Context{Name: "cli", Help: "管理中心",
return return
}}, }},
"proc": &ctx.Command{Name: "proc", Help: "进程管理", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) (e error) { "proc": &ctx.Command{Name: "proc", Help: "进程管理", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) (e error) {
m.Cmdy("cli.system", "cmd_parse", "cut", "ps", kit.Select("ax", arg, 0)) m.Cmdy("cli.system", "ps", kit.Select("ax", arg, 0), "cmd_parse", "cut")
if len(arg) > 0 { if len(arg) > 1 {
m.Cmd("select", "reg", "COMMAND", arg[1]) m.Cmd("select", "reg", "COMMAND", arg[1])
} }
return return
@ -869,7 +869,7 @@ var Index = &ctx.Context{Name: "cli", Help: "管理中心",
env := []string{ env := []string{
"cmd_env", "GOOS", goos, "cmd_env", "GOARCH", arch, "cmd_env", "GOOS", goos, "cmd_env", "GOARCH", arch,
"cmd_env", "GOTMPDIR", tmp, "cmd_env", "GOCACHE", tmp, "cmd_env", "GOTMPDIR", tmp, "cmd_env", "GOCACHE", tmp,
"cmd_env", "GOPATH", m.Conf("runtime", "boot.ctx_home")+":"+os.Getenv("GOPATH"), "cmd_env", "GOPATH", m.Conf("runtime", "boot.ctx_home") + ":" + os.Getenv("GOPATH"),
"cmd_env", "PATH", os.Getenv("PATH"), "cmd_env", "PATH", os.Getenv("PATH"),
} }

View File

@ -4,5 +4,5 @@ var version = struct {
host string host string
self int self int
}{ }{
"2019-08-30 20:40:26", "centos", 480, "2019-08-31 00:24:43", "mac", 493,
} }

View File

@ -82,46 +82,28 @@ var Index = &ctx.Context{Name: "ssh", Help: "集群中心",
Configs: map[string]*ctx.Config{ Configs: map[string]*ctx.Config{
"componet": &ctx.Config{Name: "componet", Value: map[string]interface{}{ "componet": &ctx.Config{Name: "componet", Value: map[string]interface{}{
"index": []interface{}{ "index": []interface{}{
map[string]interface{}{"componet_name": "dir", "componet_help": "目录", map[string]interface{}{"componet_name": "ifconfig", "componet_help": "ifconfig",
"componet_tmpl": "componet", "componet_view": "Context", "componet_init": "", "componet_tmpl": "componet", "componet_view": "", "componet_init": "",
"componet_type": "private", "componet_ctx": "ssh", "componet_cmd": "_route", "componet_type": "private", "componet_ctx": "ssh", "componet_cmd": "_route",
"componet_args": []interface{}{"$$", "context", "nfs", "dir", "$$", "time", "size", "line", "path"}, "inputs": []interface{}{ "componet_args": []interface{}{"_", "tcp.ifconfig"}, "inputs": []interface{}{
map[string]interface{}{"type": "text", "name": "pod", "imports": []interface{}{"plugin_you", "plugin_pod"}, "action": "auto"}, map[string]interface{}{"type": "text", "name": "pod", "value": "", "imports": "plugin_pod"},
map[string]interface{}{"type": "text", "name": "dir", "value": "usr/script", "imports": "plugin_dir", "action": "auto", "view": "long"}, map[string]interface{}{"type": "button", "value": "查看"},
map[string]interface{}{"type": "button", "value": "查看", "action": "auto"},
map[string]interface{}{"type": "button", "value": "回退", "click": "Back"},
},
"display": map[string]interface{}{"deal": "editor"},
"exports": []interface{}{"dir", "", "dir"},
"dir_root": []interface{}{"/"},
},
map[string]interface{}{"componet_name": "commit", "componet_help": "提交",
"componet_tmpl": "componet", "componet_view": "Context", "componet_init": "",
"componet_type": "private", "componet_ctx": "nfs", "componet_cmd": "git",
"componet_args": []interface{}{}, "inputs": []interface{}{
map[string]interface{}{"type": "text", "name": "dir", "view": "long"},
map[string]interface{}{"type": "select", "name": "cmd", "values": []interface{}{
"add", "commit", "checkout", "merge", "init",
}},
map[string]interface{}{"type": "text", "name": "commit", "view": "long"},
map[string]interface{}{"type": "button", "value": "执行"},
}, },
}, },
map[string]interface{}{"componet_name": "status", "componet_help": "记录", map[string]interface{}{"componet_name": "proc", "componet_help": "proc",
"componet_tmpl": "componet", "componet_view": "Context", "componet_init": "", "componet_tmpl": "componet", "componet_view": "", "componet_init": "",
"componet_type": "private", "componet_ctx": "nfs", "componet_cmd": "git", "componet_type": "private", "componet_ctx": "ssh", "componet_cmd": "_route",
"componet_args": []interface{}{}, "inputs": []interface{}{ "componet_args": []interface{}{"_", "cli.proc"}, "inputs": []interface{}{
map[string]interface{}{"type": "text", "name": "dir", "view": "long"}, map[string]interface{}{"type": "text", "name": "pod", "value": "", "imports": "plugin_pod"},
map[string]interface{}{"type": "select", "name": "cmd", "values": []interface{}{ map[string]interface{}{"type": "text", "name": "arg", "value": ""},
"branch", "status", "diff", "log", "sum", "push", "update", map[string]interface{}{"type": "text", "name": "filter", "view": "long"},
}},
map[string]interface{}{"type": "button", "value": "执行"}, map[string]interface{}{"type": "button", "value": "执行"},
}, },
}, },
map[string]interface{}{"componet_name": "spide", "componet_help": "爬虫", map[string]interface{}{"componet_name": "spide", "componet_help": "爬虫",
"componet_tmpl": "componet", "componet_view": "Context", "componet_init": "", "componet_tmpl": "componet", "componet_view": "Context", "componet_init": "",
"componet_type": "private", "componet_ctx": "ssh", "componet_cmd": "_route", "componet_type": "private", "componet_ctx": "ssh", "componet_cmd": "_route",
"componet_args": []interface{}{"$$", "context", "web", "spide"}, "inputs": []interface{}{ "componet_args": []interface{}{"_", "context", "web", "spide"}, "inputs": []interface{}{
map[string]interface{}{"type": "text", "name": "pod", "imports": "plugin_pod"}, map[string]interface{}{"type": "text", "name": "pod", "imports": "plugin_pod"},
map[string]interface{}{"type": "button", "value": "执行"}, map[string]interface{}{"type": "button", "value": "执行"},
}, },
@ -130,9 +112,9 @@ var Index = &ctx.Context{Name: "ssh", Help: "集群中心",
map[string]interface{}{"componet_name": "post", "componet_help": "请求", map[string]interface{}{"componet_name": "post", "componet_help": "请求",
"componet_tmpl": "componet", "componet_view": "Context", "componet_init": "", "componet_tmpl": "componet", "componet_view": "Context", "componet_init": "",
"componet_type": "private", "componet_ctx": "ssh", "componet_cmd": "_route", "componet_type": "private", "componet_ctx": "ssh", "componet_cmd": "_route",
"componet_args": []interface{}{"$$", "context", "web", "post", "$$", "content_type", "application/json", "parse", "json"}, "inputs": []interface{}{ "componet_args": []interface{}{"_", "web.post", "__", "content_type", "application/json", "parse", "json"}, "inputs": []interface{}{
map[string]interface{}{"type": "text", "name": "pod", "imports": "plugin_pod"}, map[string]interface{}{"type": "text", "name": "pod", "imports": "plugin_pod"},
map[string]interface{}{"type": "text", "name": "spide", "value": "zuo", "imports": "plugin_site"}, map[string]interface{}{"type": "text", "name": "spide", "value": "dev", "imports": "plugin_site"},
map[string]interface{}{"type": "text", "name": "url", "value": "/", "view": "long"}, map[string]interface{}{"type": "text", "name": "url", "value": "/", "view": "long"},
map[string]interface{}{"type": "button", "value": "执行"}, map[string]interface{}{"type": "button", "value": "执行"},
}, },
@ -140,30 +122,13 @@ var Index = &ctx.Context{Name: "ssh", Help: "集群中心",
map[string]interface{}{"componet_name": "get", "componet_help": "请求", map[string]interface{}{"componet_name": "get", "componet_help": "请求",
"componet_tmpl": "componet", "componet_view": "Context", "componet_init": "", "componet_tmpl": "componet", "componet_view": "Context", "componet_init": "",
"componet_type": "private", "componet_ctx": "ssh", "componet_cmd": "_route", "componet_type": "private", "componet_ctx": "ssh", "componet_cmd": "_route",
"componet_args": []interface{}{"$$", "context", "web", "get", "method", "GET", "parse", "json"}, "inputs": []interface{}{ "componet_args": []interface{}{"_", "web.get", "__", "method", "GET", "parse", "json"}, "inputs": []interface{}{
map[string]interface{}{"type": "text", "name": "pod", "imports": "plugin_pod"}, map[string]interface{}{"type": "text", "name": "pod", "imports": "plugin_pod"},
map[string]interface{}{"type": "text", "name": "spide", "imports": "plugin_site"}, map[string]interface{}{"type": "text", "name": "spide", "value": "dev", "imports": "plugin_site"},
map[string]interface{}{"type": "text", "name": "url", "value": "/", "view": "long"}, map[string]interface{}{"type": "text", "name": "url", "value": "/", "view": "long"},
map[string]interface{}{"type": "button", "value": "执行"}, map[string]interface{}{"type": "button", "value": "执行"},
}, },
}, },
map[string]interface{}{"componet_name": "ifconfig", "componet_help": "ifconfig",
"componet_tmpl": "componet", "componet_view": "Context", "componet_init": "",
"componet_type": "private", "componet_ctx": "tcp", "componet_cmd": "ifconfig",
"componet_args": []interface{}{}, "inputs": []interface{}{
map[string]interface{}{"type": "button", "value": "网卡"},
},
},
map[string]interface{}{"componet_name": "proc", "componet_help": "proc",
"componet_tmpl": "componet", "componet_view": "Company", "componet_init": "",
"componet_type": "private", "componet_ctx": "ssh", "componet_cmd": "_route",
"componet_args": []interface{}{"$$", "context", "cli", "proc"}, "inputs": []interface{}{
map[string]interface{}{"type": "text", "name": "pod", "value": "", "imports": "plugin_pod"},
map[string]interface{}{"type": "text", "name": "cmd", "value": ""},
map[string]interface{}{"type": "text", "name": "cmd", "view": "long"},
map[string]interface{}{"type": "button", "value": "执行"},
},
},
}, },
}, Help: "组件列表"}, }, Help: "组件列表"},

View File

@ -99,7 +99,7 @@ func (web *WEB) Login(msg *ctx.Message, w http.ResponseWriter, r *http.Request)
// defer func() { // defer func() {
// msg.Log("info", "access: %s", msg.Option("access", msg.Cmdx("aaa.sess", "access"))) // msg.Log("info", "access: %s", msg.Option("access", msg.Cmdx("aaa.sess", "access")))
// }() // }()
if msg.Options("username") && msg.Options("password") { if msg.Options("username") && msg.Options("password") {
if msg.Cmds("aaa.auth", "username", msg.Option("username"), "password", msg.Option("password")) { if msg.Cmds("aaa.auth", "username", msg.Option("username"), "password", msg.Option("password")) {
msg.Log("info", "login: %s", msg.Option("username")) msg.Log("info", "login: %s", msg.Option("username"))
http.SetCookie(w, &http.Cookie{Name: "sessid", Value: msg.Cmdx("aaa.user", "session", "select"), Path: "/"}) http.SetCookie(w, &http.Cookie{Name: "sessid", Value: msg.Cmdx("aaa.user", "session", "select"), Path: "/"})
@ -657,25 +657,23 @@ var Index = &ctx.Context{Name: "web", Help: "应用中心",
m.Log("info", "parse: %s content: %s", parse, ct) m.Log("info", "parse: %s content: %s", parse, ct)
switch { switch {
// 解析数据
case parse == "json" || strings.HasPrefix(ct, "application/json") || strings.HasPrefix(ct, "application/javascript"): case parse == "json" || strings.HasPrefix(ct, "application/json") || strings.HasPrefix(ct, "application/javascript"):
// 解析数据
if json.NewDecoder(res.Body).Decode(&result); m.Options("temp_expire") { if json.NewDecoder(res.Body).Decode(&result); m.Options("temp_expire") {
if !m.Has("temp") { if !m.Has("temp") {
m.Option("temp", "") m.Option("temp", "")
} }
m.Put("option", "data", result).Cmdy("mdb.temp", "url", uri+uri_arg, "data", "data", m.Meta["temp"]) m.Put("option", "data", result).Cmdy("mdb.temp", "url", uri+uri_arg, "data", "data", m.Meta["temp"])
} else { break
} else if result != nil {
if b, e := json.MarshalIndent(result, "", " "); m.Assert(e) { if b, e := json.MarshalIndent(result, "", " "); m.Assert(e) {
m.Echo(string(b)) m.Echo(string(b))
} }
break
} }
fallthrough
// 解析网页
case parse == "html":
parseHTML()
// 输出数据
default: default:
// 输出数据
if buf, e := ioutil.ReadAll(res.Body); m.Assert(e) { if buf, e := ioutil.ReadAll(res.Body); m.Assert(e) {
m.Echo(string(buf)) m.Echo(string(buf))
} }

View File

@ -314,6 +314,7 @@ var Index = &ctx.Context{Name: "yac", Help: "语法中心",
map[string]interface{}{"page": "key", "hash": "key", "word": []interface{}{"[A-Za-z_][A-Za-z_0-9]*"}}, map[string]interface{}{"page": "key", "hash": "key", "word": []interface{}{"[A-Za-z_][A-Za-z_0-9]*"}},
map[string]interface{}{"page": "str", "hash": "str", "word": []interface{}{"mul{", "\"[^\"]*\"", "'[^']*'", "}"}}, map[string]interface{}{"page": "str", "hash": "str", "word": []interface{}{"mul{", "\"[^\"]*\"", "'[^']*'", "}"}},
map[string]interface{}{"page": "exe", "hash": "exe", "word": []interface{}{"mul{", "$", "@", "}", "opt{", "key", "}"}}, map[string]interface{}{"page": "exe", "hash": "exe", "word": []interface{}{"mul{", "$", "@", "}", "opt{", "key", "}"}},
map[string]interface{}{"page": "exe", "hash": "exe", "word": []interface{}{"mul{", "$", "@", "}", "opt{", "num", "}"}},
// 表达式语句 // 表达式语句
map[string]interface{}{"page": "op1", "hash": "op1", "word": []interface{}{"mul{", "-", "+", "}"}}, map[string]interface{}{"page": "op1", "hash": "op1", "word": []interface{}{"mul{", "-", "+", "}"}},
@ -1075,12 +1076,12 @@ var Index = &ctx.Context{Name: "yac", Help: "语法中心",
} }
return return
}}, }},
"fun": &ctx.Command{Name: "fun", Help: "", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) (e error) { "fun": &ctx.Command{Name: "fun name help", Help: "小函数", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) (e error) {
p := m.Optionv("bio.stack").(*kit.Stack).Push(arg[0], false, m.Optioni("stack.pos")) p := m.Optionv("bio.stack").(*kit.Stack).Push(arg[0], false, m.Optioni("stack.pos"))
m.Log("stack", "push %v", p.String("\\")) m.Log("stack", "push %v", p.String("\\"))
if len(arg) > 2 { if len(arg) > 2 {
m.Cmd("kit", "kit", arg[1:6], arg[1], arg[6:]) m.Cmd("kit", "kit", arg[1:])
} }
self := &ctx.Command{Name: strings.Join(arg[1:], " "), Help: []string{"pwd", "ls"}} self := &ctx.Command{Name: strings.Join(arg[1:], " "), Help: []string{"pwd", "ls"}}
self.Hand = func(m *ctx.Message, c *ctx.Context, key string, arg ...string) (e error) { self.Hand = func(m *ctx.Message, c *ctx.Context, key string, arg ...string) (e error) {
@ -1092,7 +1093,7 @@ var Index = &ctx.Context{Name: "yac", Help: "语法中心",
p.Data = self p.Data = self
return return
}}, }},
"kit": &ctx.Command{Name: "kit name help [view [init]] [public|protected|private] cmd arg... [input value [key val]...]...", Help: "小功能", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) (e error) { "kit": &ctx.Command{Name: "kit name help [init [show]] [public|protected|private] cmd arg... [input value [key val]...]...", Help: "小功能", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) (e error) {
m.Log("info", "_index: %v", arg) m.Log("info", "_index: %v", arg)
args := []interface{}{} args := []interface{}{}
@ -1109,14 +1110,14 @@ var Index = &ctx.Context{Name: "yac", Help: "语法中心",
default: default:
switch arg[4] { switch arg[4] {
case "private", "protected", "public": case "private", "protected", "public":
begin, view, show, cmd = 6, arg[3], arg[4], arg[5] begin, init, show, cmd = 6, arg[3], arg[4], arg[5]
default: default:
begin, view, init, show, cmd = 7, arg[3], arg[4], arg[5], arg[6] begin, init, view, show, cmd = 7, arg[3], arg[4], arg[5], arg[6]
} }
} }
if m.Confs("input", cmd) { if m.Confs("input", cmd) {
cmd, begin = arg[1], begin - 1 cmd, begin = arg[1], begin-1
} }
for i := begin; i < len(arg); i++ { for i := begin; i < len(arg); i++ {

View File

@ -1,24 +1,43 @@
fun clip "粘贴板" "" "" \ fun qrcode "二维码" "index.js" public \
public \ text "" name txt view long \
text "" name you imports plugin_you action auto \
text "" view long \
button "存储"
copy ssh._route $1 aaa.clip $2
end
fun qrcode "二维码" "" "index.js" \
public \
text "" view long \
button "生成" button "生成"
let url = $1 || $referer append qrcode ($1 || $referer)
append qrcode $url
end end
fun salary "工资单" "" "" \ kit clip "粘贴板" public "ssh._route" _ "aaa.clip" \
public \ text "" name you imports plugin_you action auto \
text "" name txt view long \
button "保存"
kit macro "脚本宏" "macro.js" public \
text "" name mac imports plugin_mac action auto \
button "查看" \
button "全部" click "all" \
button "录制" click "Record" \
button "运行" click "Replay" \
exports mac name
kit sys "命令行" public "nfs.source" \
textarea "" view long \
button "运行" \
exports mac name
fun note "记事本" public \
text "" name tip imports plugin_tip view long action auto \
button "创建" action auto \
exports tip file tip \
feature display inner
if $1 == "" || $1 ~ ".*/$"
copy context wiki tree __
else
copy context wiki text __
end
end
fun salary "工资单" public \
text "9000" label total \ text "9000" label total \
text "9000" label base \ text "9000" label base \
button "计算" button "计算"
@ -51,45 +70,3 @@ fun salary "工资单" "" "" \
append "结余" $rest append "结余" $rest
end end
fun macro "脚本宏" "" "macro.js" \
public \
text "" name mac imports plugin_mac action auto \
button "查看" \
button "全部" click "all" \
button "录制" click "Record" \
button "运行" click "Replay" \
exports mac name
end
fun sys "命令行" "" "" \
public \
textarea "" view long \
button "运行" \
exports mac name
copy nfs.source system __
end
fun note "记事本" "" "" \
public \
text "" name tip imports plugin_tip view long action auto \
button "创建" action auto \
feature display inner \
exports tip file tip
if $1 == "" || $1 ~ ".*/$"
copy context wiki tree __
else
copy context wiki text __
end
end
fun hello world "" "" \
public \
textarea "" \
button "执行"
copy pwd
end

View File

@ -711,10 +711,11 @@ function Plugin(page, pane, field, runs) {
}) })
var count = kit.Selector(option, "args").length var count = kit.Selector(option, "args").length
args && count < args.length && (item.value = value||args[count++]||item.value||"") args && count < args.length && (item.value = value||args[count++]||item.value||"");
(item.title || item.name) && (item.title = item.title || item.name)
item.title && (item.placeholder = item.title)
item.title = item.title || item.name || ""
item.placeholder = item.title
name = item.name || "input" name = item.name || "input"
var input = {type: "input", name: name, data: item} var input = {type: "input", name: name, data: item}
switch (item.type) { switch (item.type) {
@ -820,10 +821,11 @@ function Plugin(page, pane, field, runs) {
}, time) }, time)
}, },
Check: function(target, cb) { Check: function(target, cb) {
plugin.Select(true), option.querySelectorAll(".args").forEach(function(item, index, list) { plugin.Select(true), kit.Selector(option, ".args", function(item, index, list) {
target == undefined && index == list.length-1 && plugin.Runs(window.event, cb) target == undefined && index == list.length-1 && plugin.Runs(window.event, cb)
item == target && (index == list.length-1? plugin.Runs(window.event, cb): page.plugin == field && list[index+1].focus()) item == target && (index == list.length-1? plugin.Runs(window.event, cb): page.plugin == field && list[index+1].focus())
}) return item
}).length == 0 && plugin.Runs(window.event, cb)
}, },
Runs: function(event, cb) { Runs: function(event, cb) {
plugin.Run(event, kit.Selector(option, ".args", function(item, index) {return item.value}), cb) plugin.Run(event, kit.Selector(option, ".args", function(item, index) {return item.value}), cb)

View File

@ -339,7 +339,7 @@ kit = toolkit = {
var kit = this var kit = this
var tr = kit.AppendChild(table, "tr") var tr = kit.AppendChild(table, "tr")
fields.forEach(function(key, j) { fields.forEach(function(key, j) {
var td = kit.AppendChild(tr, "th", key) var td = kit.AppendChild(tr, "th", kit.Color(key))
}) })
data.forEach(function(row, i) { data.forEach(function(row, i) {
var tr = kit.AppendChild(table, "tr", {className: "normal"}) var tr = kit.AppendChild(table, "tr", {className: "normal"})