From 172b2174f6d542046b9699e2f2ecb5fba5dcdce8 Mon Sep 17 00:00:00 2001 From: harveyshao Date: Thu, 10 Nov 2022 15:22:33 +0800 Subject: [PATCH] opt draw.js --- plugin/local/wiki/data.js | 1 + plugin/local/wiki/draw.js | 8 +- plugin/story/pie.js | 46 +++++------- plugin/story/spide.js | 152 +++++++++++--------------------------- plugin/story/trend.js | 146 +++++++++++++++--------------------- proto.js | 10 ++- 6 files changed, 133 insertions(+), 230 deletions(-) diff --git a/plugin/local/wiki/data.js b/plugin/local/wiki/data.js index a3515658..8eab5e08 100644 --- a/plugin/local/wiki/data.js +++ b/plugin/local/wiki/data.js @@ -10,6 +10,7 @@ Volcanos(chat.ONIMPORT, {_init: function(can, msg, cb, target) { can.onmotion.cl }, _story: function(can, msg, display) { return can.onappend.plugin(can, {msg: msg, mode: chat.OUTPUT, display: can.misc.MergePath(can, display, chat.PLUGIN_STORY)}) }, "折线图": function(can, msg, field) { return can.onimport._story(can, msg, can.base.MergeURL("trend.js", {field: field, view: "折线图"})) }, + "柱状图": function(can, msg, field) { return can.onimport._story(can, msg, can.base.MergeURL("trend.js", {field: field, view: "柱状图"})) }, "比例图": function(can, msg, field) { return can.onimport._story(can, msg, can.base.MergeURL("pie.js", {field: field})) }, }, [""]) Volcanos(chat.ONFIGURE, { diff --git a/plugin/local/wiki/draw.js b/plugin/local/wiki/draw.js index 8ce6c5b3..3b0fc4bd 100644 --- a/plugin/local/wiki/draw.js +++ b/plugin/local/wiki/draw.js @@ -53,15 +53,15 @@ Volcanos(chat.ONIMPORT, {_init: function(can, msg, target) { can.onmotion.clear( can._plugins = (can._plugins||[]).concat(sub) }, can.ui.display): can.onimport.layout(can) }, - block: function(can, type, value, group) { group = group||can.group||can.svg + block: function(can, type, value, group) { group = group||can.group var target = document.createElementNS("http://www.w3.org/2000/svg", type) return group.appendChild(can.onimport._block(can, target)), target.Value(value), target }, - group: function(can, name, value, group) { var target = can.onimport.block(can, svg.G, value, group) + group: function(can, name, value, group) { var target = can.onimport.block(can, svg.G, value, group||can.svg) return target.Value(html.CLASS, name), can.onimport._group(can, target).click(), target }, - draw: function(event, can, value, group) { - var figure = can.onfigure[value.shape], data = figure.draw(event, can, value.points, value.style||{}); can.core.Item(value.style, function(key, value) { data[key] = value }) + draw: function(can, value, group) { group = group||can.svg + var figure = can.onfigure[value.shape], data = figure.draw({}, can, value.points, value.style||{}); can.core.Item(value.style, function(key, value) { data[key] = value }) var target = can.onimport.block(can, figure.data.name||value.shape, data, group); can.core.ItemCB(value, function(key, cb) { target[key] = cb }) return value._init && value._init(target), target }, diff --git a/plugin/story/pie.js b/plugin/story/pie.js index 96d99f84..77977ecc 100644 --- a/plugin/story/pie.js +++ b/plugin/story/pie.js @@ -1,42 +1,26 @@ -Volcanos(chat.ONIMPORT, {help: "导入数据", _init: function(can, msg, cb, target) { can.onmotion.clear(can) - var color = [cli.RED, cli.YELLOW, cli.GREEN, cli.CYAN, cli.BLUE, cli.PURPLE] - var color = ["#3300FF", "#2196F3", "#4CAF50", "#CDDC39", "#FFEB3B", "#9C27B0", "#795548", "#607D8B", "#CC33FF"] - var height = msg.Option(html.HEIGHT)||can.ConfHeight() - msg = can.sup.__msg||msg - can.page.ClassList.add(can, can._fields, "draw"), can.onmotion.hidden(can, can._action) - can.require(["/plugin/local/wiki/draw.js", "/plugin/local/wiki/draw/path.js"], function() { can.onimport._show(can, msg) - var margin = height/8, r = height/2-margin; can.svg.Val(html.WIDTH, 2*(r+margin)), can.svg.Val(html.HEIGHT, 2*(r+margin)) - can.onimport._draw(can, msg, can.Conf(mdb.FIELD), color, r+margin, r+margin, r, margin, 0) - - can.page.style(can, can.ui.project, html.MAX_WIDTH, can.ConfWidth()-height) - can.onmotion.clear(can, can.ui.project), can.onmotion.toggle(can, can.ui.project, true) +Volcanos(chat.ONIMPORT, {_init: function(can, msg, cb, target) { + can.requireDraw(function() { + can.onimport.layout(can), can.onmotion.clear(can, can.ui.project), can.onmotion.toggle(can, can.ui.project, true) can.onappend.table(can, msg, null, can.ui.project), can.page.Select(can, can.ui.project, html.TR, function(tr, index) { - can.page.Modify(can, tr, {onmouseenter: function(event) { can.onimport._draw(can, msg, can.Conf(mdb.FIELD), color, r+margin, r+margin, r, margin, index-1) }}) - }) - can.base.isFunc(cb) && cb(msg), can.onappend._status(can, [].concat(msg.append, ["weight"])) + can.page.Modify(can, tr, {onmouseenter: function(event) { can._draw(index-1) }}) + }), can.base.isFunc(cb) && cb(msg), can.onappend._status(can, [].concat(msg.append, ["weight"])) }) }, - _draw: function(can, msg, field, color, x, y, r, margin, which) { field = field||mdb.VALUE + _draw: function(can, msg, field, color, x, y, r, margin, which) { if (which == can._last) { return } can._last = which, field = field||mdb.VALUE + if (msg.Length() == 1) { return can.onimport.draw(can, {shape: svg.CIRCLE, point: [{x: x, y: y}, {x: x, y: y+r}], style: {fill: color[0]}}) } function pos(x, y, r, angle) { angle -= 90; return [x + r * Math.cos(angle * Math.PI / 180), y + r * Math.sin(angle * Math.PI / 180)] } - function pie(x, y, r, begin, span, color, cb) { can.onimport.draw({}, can, {shape: svg.PATH, style: kit.Dict( - svg.STROKE_WIDTH, 1, svg.STROKE, color, svg.FILL, color, "d", can.base.joins([ + function pie(x, y, r, begin, span, color, cb) { can.onimport.draw(can, {shape: svg.PATH, style: kit.Dict( + svg.STROKE, color, svg.FILL, color, "d", can.base.joins([ ["M", x, y], ["L"].concat(pos(x, y, r, begin)), ["A", r, r, "0", span>180? "1": "0", "1"].concat(pos(x, y, r, begin+span)), ["Z"] ], ice.SP, ice.FS), ), onmouseenter: function(event) { can.base.isFunc(cb) && cb(event) } }) } - if (which == can._last) { return } can._last = which can.onmotion.clear(can, can.svg), can.svg.Value(mdb.COUNT, 0) var total = 0; msg.Table(function(value) { total += can.onimport._parseInt(can, value[field]) }) var begin = 0; msg[cli.COLOR] = [], msg["weight"] = [], msg.Table(function(value, index) { var span = can.onimport._parseInt(can, value[field])/total*360 var p = index==which? pos(x, y, margin, begin+span/2): [x, y], c = color[index%color.length] - - if (msg.Length() == 1) { - can.onimport.draw({}, can, {shape: svg.CIRCLE, point: [{x: x, y: y}, {x: x, y: y+r}], style: {fill: color[0]}}) - } else { - pie(p[0], p[1], r, begin, span, c, function(event) { can.onimport._draw(can, msg, field, color, x, y, r, margin, index) }), begin += span - } - msg.Push(cli.COLOR, ' ') - msg.Push("weight", parseInt(span*100/360)+"%") + pie(p[0], p[1], r, begin, span, c, function(event) { can.onimport._draw(can, msg, field, color, x, y, r, margin, index) }), begin += span + msg.Push(cli.COLOR, ' ').Push("weight", parseInt(span*100/360)+"%") if (index == which) { can.Status(value), can.Status("weight", parseInt(span*100/360)+"%") } }) }, @@ -46,6 +30,10 @@ Volcanos(chat.ONIMPORT, {help: "导入数据", _init: function(can, msg, cb, tar if (can.base.endWith(value, "g")) { return parseInt(value)*1000000000 } if (can.base.endWith(value, "m")) { return parseInt(value)*1000000 } return parseInt(value) - } + }, + layout: function(can) { var color = ["#3300FF", "#2196F3", "#4CAF50", "#CDDC39", "#FFEB3B", "#9C27B0", "#795548", "#607D8B", "#CC33FF"] + var height = can.base.Max(can.ConfHeight(), can.ConfWidth()), margin = 20, r = height/2-margin; can.svg.Val(html.WIDTH, height), can.svg.Val(html.HEIGHT, height) + can._draw = function(which) { can.onimport._draw(can, can._msg, can.Conf(mdb.FIELD), color, r+margin, r+margin, r, margin, which) }, can._draw(0) + can.page.style(can, can.ui.project, html.MAX_WIDTH, can.ConfWidth()-height) + }, }) -Volcanos(chat.ONEXPORT, {help: "导出数据", _show: function(can) {}}) diff --git a/plugin/story/spide.js b/plugin/story/spide.js index 8cdcdafc..3efa6bfd 100644 --- a/plugin/story/spide.js +++ b/plugin/story/spide.js @@ -1,129 +1,65 @@ -Volcanos(chat.ONIMPORT, {help: "导入数据", _init: function(can, msg, cb, target) { - can.onmotion.clear(can); if (msg.Length() == 0) { return } - - can.ConfDefault({root: "ice", field: msg.append[0], split: ice.PS}) - can.dir_root = msg.Option(nfs.DIR_ROOT)||can.Conf("root") - can._tree = can.onimport._tree(can, msg.Table(), can.Conf(mdb.FIELD), can.Conf(lex.SPLIT)) - if (!can._tree[""]) { return } can._tree[""].name = can.Conf("root") - - can.size = parseInt(can.Action("size")||24) - can.margin = parseInt(can.Action("margin")||30) - can.page.ClassList.add(can, can._fields, "draw") - can.require(["/plugin/local/wiki/draw.js", "/plugin/local/wiki/draw/path.js"], function() { +Volcanos(chat.ONIMPORT, {_init: function(can, msg, cb) { + can.requireDraw(function() { can.ConfDefault({field: msg.append[0], split: ice.PS}) + can.dir_root = can.Conf(nfs.ROOT)||msg.Option(nfs.DIR_ROOT), can._tree = can.onimport._tree(can, msg.Table(), can.Conf(mdb.FIELD), can.Conf(lex.SPLIT)) can.base.isFunc(cb) && cb(msg), can.onimport.layout(can) }) }, _tree: function(can, list, field, split) { - var node = {}; can.core.List(list, function(item) { if (!item[field]) { return } - can.core.List(can.base.trimPrefix(item[field], can.dir_root+split).split(split), function(value, index, array) { - var last = array.slice(0, index).join(split)||"", name = array.slice(0, index+1).join(split) - if (!value || node[name]) { return } - - node[last] = node[last]||{name: last, meta: {}, list: []} - node[last].list.push(node[name] = { - name: value+(index==array.length-1? "": split), - meta: item, list: [], last: node[last], - file: item[field]||item.file, hide: true, - }) + var node = {}; can.core.List(list, function(item) { can.core.List(item[field].split(split), function(value, index, array) { + var last = array.slice(0, index).join(split)||can.dir_root, name = array.slice(0, index+1).join(split) + value && !node[name] && (node[last] = node[last]||{name: last, meta: {}, list: []}).list.push(node[name] = { + name: value+(index==array.length-1? "": split), file: item[field]||item.file, hide: true, meta: item, list: [], last: node[last], }) - }) - return node + }) }); return node }, - _height: function(can, tree, deep) { if (!tree) { return 0 } - tree.deep = deep||0 - if (!tree.list || tree.list.length == 0 || tree.hide) { return tree.height = 1 } - - var height = 0; can.core.List(tree.list, function(item) { - height += can.onimport._height(can, item, (deep||0)+1) - }) - return tree.height = height + _height: function(can, tree, deep) { tree.deep = deep||0, tree.height = 0; if (tree.list.length == 0 || tree.hide) { return tree.height = 1 } + can.core.List(tree.list, function(item) { tree.height += can.onimport._height(can, item, (deep||0)+1) }); return tree.height }, - _width: function(can, tree, deep) { if (!tree) { return 0 } - tree.deep = deep||0 - if (!tree.list || tree.list.length == 0 || tree.hide) { if (!tree.name) { return tree.width = 20 } - tree.view = can.onimport.draw({}, can, {shape: html.TEXT, point: [{x: 0, y: 0}], style: {inner: tree.name}}) - return tree.width = tree.view.Val("textLength")+can.margin - } - - var width = 0; can.core.List(tree.list, function(item) { - width += can.onimport._width(can, item, (deep||0)+1) - }) - return tree.width = width + _width: function(can, tree, deep) { tree.deep = deep||0, tree.width = 0; if (tree.list.length == 0 || tree.hide) { + return tree.width = can.onimport.draw(can, {shape: html.TEXT, points: [{x: 0, y: 0}], style: {inner: tree.name}}).Val(svg.TEXT_LENGTH)+can.margin + } can.core.List(tree.list, function(item) { tree.width += can.onimport._width(can, item, (deep||0)+1) }); return tree.width }, - _color: function(can, tree) { - return tree.meta&&tree.meta.color || (tree.list == 0? cli.PURPLE: cli.YELLOW) - }, - layout: function(can) { can.margin = can.margin||20 - can.onmotion.clear(can), can.onimport._show(can, can.request()) - can.svg.Val(svg.FONT_SIZE, can.Action("size")) - can.page.style(can, can._output, html.MAX_HEIGHT, "") + _color: function(can, tree) { return tree.meta.color || (tree.list == 0? cli.PURPLE: cli.YELLOW) }, + layout: function(can) { can.page.style(can, can._output, html.MAX_HEIGHT, "") + can.svg.Val(svg.FONT_SIZE, can.size = parseInt(can.Action(html.SIZE))), can.margin = parseInt(can.Action(html.MARGIN)) can.onaction[can.Action(ice.VIEW)](event, can, can.Action(ice.VIEW)) }, }, [""]) -Volcanos(chat.ONACTION, {help: "用户操作", list: [ - [ice.VIEW, "横向", "纵向"], ["size", 24, 32, 48], ["margin", 30, 50, 100], - ], - size: function(event, can) { can.size = parseInt(can.Action("size")||30), can.onimport.layout(can) }, - margin: function(event, can) { can.margin = parseInt(can.Action("margin")||30), can.onimport.layout(can) }, - "横向": function(event, can, button) { - can.onimport._height(can, can._tree[""]) - can.sup.view = button, can.onmotion.clear(can, can.svg) - - can.svg.Val(html.HEIGHT, can._tree[""].height*can.margin+2*can.margin) - can.width = 0, can.onaction._draw_horizontal(can, can._tree[""], can.margin, can.margin) - can.svg.Val(html.WIDTH, can.width+can.margin) +Volcanos(chat.ONACTION, {list: [[ice.VIEW, "横向", "纵向"], [html.SIZE, 24, 32, 48], [html.MARGIN, 10, 30, 50]], + size: function(event, can) { can.onimport.layout(can) }, margin: function(event, can) { can.onimport.layout(can) }, + "横向": function(event, can, button) { can.onimport._height(can, can._tree[can.dir_root]), can.onmotion.clear(can, can.svg) + can.svg.Val(html.HEIGHT, can._tree[can.dir_root].height*(can.size+can.margin)+2*can.margin), can.svg.Value(svg.TEXT_ANCHOR, "start") + can.onaction._draw_horizontal(can, can._tree[can.dir_root], can.margin, can.margin) }, - "纵向": function(event, can, button) { - can.onimport._width(can, can._tree[""]) - can.sup.view = button, can.onmotion.clear(can, can.svg) - - can.svg.Val(html.WIDTH, can._tree[""].width+2*can.margin) - can.height = 0, can.onaction._draw_vertical(can, can._tree[""], can.margin, can.margin+can.margin) - can.svg.Val(html.HEIGHT, can.height+can.margin) + "纵向": function(event, can, button) { can.onimport._width(can, can._tree[can.dir_root]), can.onmotion.clear(can, can.svg) + can.svg.Val(html.WIDTH, can._tree[can.dir_root].width+2*can.margin), can.svg.Value(svg.TEXT_ANCHOR, "middle") + can.onaction._draw_vertical(can, can._tree[can.dir_root], can.margin, can.margin+(can.size+can.margin)/2) }, _draw: function(can, tree, x, y, style) { var color = can.onimport._color(can, tree) - if (!tree.name) { return } - tree.view = can.onimport.draw({}, can, {shape: html.TEXT, point: [{x: x, y: y}], style: can.base.Copy(kit.Dict( - svg.STROKE, color, svg.FILL, color, svg.TEXT_ANCHOR, "start", "inner", tree.name||tree.file, - ), style), }), can.core.ItemCB(can.ondetail, tree.view, can, tree) + tree.view = can.onimport.draw(can, {shape: html.TEXT, points: [{x: x, y: y}], style: can.base.Copy(kit.Dict(svg.STROKE, color, svg.FILL, color, html.INNER, tree.name), style)}) + return can.core.ItemCB(can.ondetail, tree.view, can, tree), tree.view }, - _draw_vertical: function(can, tree, x, y) { tree.x = x, tree.y = y - can.onaction._draw(can, tree, x+tree.width/2, y, kit.Dict(svg.TEXT_ANCHOR, "middle")) - - tree.height = can.margin - if (y+tree.height > can.height) { can.height = y+tree.height } - if (tree.hide) { return } - - var offset = 0; can.core.List(tree.list, function(item) { if (!item) { return } - item.name && item.name != " " && can.onimport.draw({}, can, {shape: svg.PATH2V, point: [ - {x: x+tree.width/2, y: y+tree.height-can.margin/2}, - {x: x+offset+item.width/2, y: y+tree.height+can.margin/2}, - ], style: {stroke: cli.CYAN}}) - can.onaction._draw_vertical(can, item, x+offset, y+tree.height+can.margin), offset += item.width + _draw_vertical: function(can, tree, x, y) { + tree.height = can.size+can.margin, can.onaction._draw(can, tree, tree.x = x+tree.width/2, tree.y = y); if (y+tree.height > can.svg.Val(html.HEIGHT)) { can.svg.Val(html.HEIGHT, y+tree.height) } + var offset = 0; tree.hide || can.core.List(tree.list, function(item) { + can.onimport.draw(can, {shape: svg.PATH2V, points: [ + {x: x+tree.width/2, y: y+tree.height/2-can.margin/2}, {x: x+offset+item.width/2, y: y+tree.height/2+8*can.margin-can.margin/2}, + ], style: {stroke: cli.CYAN}}), can.onaction._draw_vertical(can, item, x+offset, y+tree.height+8*can.margin), offset += item.width }) }, - _draw_horizontal: function(can, tree, x, y) { tree.x = x, tree.y = y - can.onaction._draw(can, tree, x, y+tree.height*can.margin/2, kit.Dict(svg.TEXT_ANCHOR, "start")) - - tree.width = tree.view&&tree.view.Val("textLength")||(tree.name||"").length*10 - if (x+tree.width > can.width) { can.width = x+tree.width } - if (tree.hide) { return } - - var offset = 0; can.core.List(tree.list, function(item) { if (!item || !item.name) { return } - can.onimport.draw({}, can, {shape: svg.PATH2H, point: [ - {x: x+tree.width+can.margin/8, y: y+tree.height*can.margin/2}, - {x: x+tree.width+can.margin*2-2*can.margin/8, y: y+offset+item.height*can.margin/2} - ], style: {stroke: cli.CYAN}}) - - can.onaction._draw_horizontal(can, item, x+tree.width+2*can.margin, y+offset) - offset += item.height*can.margin + _draw_horizontal: function(can, tree, x, y) { + tree.width = can.onaction._draw(can, tree, tree.x = x, tree.y = y+tree.height*(can.size+can.margin)/2).Val(svg.TEXT_LENGTH); if (x+tree.width > can.svg.Val(html.WIDTH)) { can.svg.Val(html.WIDTH, x+tree.width) } + var offset = 0; tree.hide || can.core.List(tree.list, function(item) { + can.onimport.draw(can, {shape: svg.PATH2H, points: [ + {x: x+tree.width+can.margin/2, y: y+tree.height*(can.size+can.margin)/2}, {x: x+tree.width+8*can.margin-can.margin/2, y: y+offset+item.height*(can.size+can.margin)/2} + ], style: {stroke: cli.CYAN}}), can.onaction._draw_horizontal(can, item, x+tree.width+8*can.margin, y+offset), offset += item.height*(can.size+can.margin) }) }, }) -Volcanos(chat.ONDETAIL, {help: "用户交互", - onmouseenter: function(event, can, tree) { var y = tree.y+tree.height*can.margin/2 - can.page.Remove(can, can.pos), can.pos = can.onimport.draw({}, can, {shape: svg.RECT, point: [ - {x: tree.x-can.margin/4, y: y-can.margin/2}, {x: tree.x+tree.width+can.margin/8, y: y+can.margin/2}, +Volcanos(chat.ONDETAIL, { + onmouseenter: function(event, can, tree) { var view = tree.view, width = can.Action(ice.VIEW) == "纵向"? view.Val(svg.TEXT_LENGTH)/2: 0 + can.page.Remove(can, can.pos), can.pos = can.onimport.draw(can, {shape: svg.RECT, points: [ + {x: view.Val(svg.X)-width-can.margin/2, y: view.Val(svg.Y)-(can.size+can.margin/2)/2}, {x: view.Val(svg.X)-width+view.Val(svg.TEXT_LENGTH)+can.margin/2, y: view.Val(svg.Y)+(can.size+can.margin/2)/2}, ], style: {stroke: cli.RED, fill: html.NONE}}), can.onkeymap.prevent(event) }, onclick: function(event, can, tree) { @@ -138,7 +74,7 @@ Volcanos(chat.ONDETAIL, {help: "用户交互", if (msg.Append(mdb.INDEX)) { msg.Table(function(value) { can.ondetail.plugin(can, value.index, []) }); return } if (msg.Option(lex.SPLIT)) { - tree.list = can.onimport._tree(can, msg.Table(), msg.Option(mdb.FIELD)||msg.append[0], msg.Option(lex.SPLIT))[""].list||[] + tree.list = can.onimport._tree(can, msg.Table(), msg.Option(mdb.FIELD)||msg.append[0], msg.Option(lex.SPLIT))[can.dir_root].list||[] can.core.List(tree.list, function(item) { item.last = tree }) } else { msg.Table(function(item) { tree.list.push({ @@ -151,7 +87,7 @@ Volcanos(chat.ONDETAIL, {help: "用户交互", }, true) }, plugin: function(can, index, args, prefix) { - can.onappend.plugin(can, {type: chat.STORY, mode: chat.FLOAT, index: index, args: args}, function(sub) { + can.onappend.plugin(can, {mode: chat.FLOAT, index: index, args: args}, function(sub) { sub.run = function(event, cmds, cb) { can.runAction(can.request(event), prefix||[ice.RUN, index], cmds, cb) } sub.onaction.close = function() { can.page.Remove(can, sub._target) } can.getActionSize(function(left, top, width, height) { left = left||0 diff --git a/plugin/story/trend.js b/plugin/story/trend.js index ecf5ad44..45535aab 100644 --- a/plugin/story/trend.js +++ b/plugin/story/trend.js @@ -1,139 +1,111 @@ -Volcanos(chat.ONIMPORT, {help: "导入数据", _init: function(can, msg, cb, target) { - can.onmotion.clear(can), can.page.ClassList.add(can, can._fields, "draw") - can.require(["/plugin/local/wiki/draw.js", "/plugin/local/wiki/draw/path.js"], function() { - can.data = msg.Table(), can.onimport._sum(can) - can.base.isFunc(cb) && cb(msg), can.Conf(ice.VIEW) && can.Action(ice.VIEW, can.Conf(ice.VIEW)) - can.list = can.sup.list||can.list - can.onimport.layout(can), can.base.isFunc(cb) && cb(msg) - can.data = msg.Table(), can.onimport._sum(can) +Volcanos(chat.ONIMPORT, {_init: function(can, msg, cb) { + can.requireDraw(function() { + can.data = msg.Table(), can.base.isFunc(cb) && cb(msg), can.onimport.layout(can) }) }, - _sum: function(can) { + _sum: function(can) { if (can.list) { return can.list } var begin = "", count = 0, rest = 0, add = 0, del = 0, max = 0 can.max = 0, can.min = 0, can.list = can.core.List(can.data, function(value, index) { - var line = { + var item = { date: value[can._msg.append[0]], text: value[can._msg.append[4]], add: parseInt(value[can._msg.append[1]]), del: parseInt(value[can._msg.append[2]]), } - line.begin = rest - line.max = rest + line.add - line.min = rest - line.del - line.close = rest + line.add - line.del + item.begin = rest + item.max = rest + item.add + item.min = rest - item.del + item.close = rest + item.add - item.del begin = begin || value.date, count++ - rest = line.close, add += line.add, del += line.del + rest = item.close, add += item.add, del += item.del - if (line.max - line.min > max) { max = line.max - line.min } - if (line.max > can.max) { can.max = line.max } - if (line.min < can.min) { can.min = line.min } - return line + if (item.max - item.min > max) { max = item.max - item.min } + if (item.max > can.max) { can.max = item.max } + if (item.min < can.min) { can.min = item.min } + return item }) can.Status({"from": begin, "commit": count, "total": add+del, "max": max}) + return can.list }, _layout: function(can) { - var height = can.onexport.height(can) - var width = parseInt(can.ConfWidth()), space = parseInt(can.Action("space")||"10") - var step = parseFloat((width-2*space) / can.list.length) - + var height = can.onexport.height(can), width = parseInt(can.ConfWidth()) can.onmotion.clear(can, can._output), can.onimport._show(can, can._msg) can.svg.Val(html.HEIGHT, height), can.svg.Val(html.WIDTH, width) - return {height: height, width: width, space: space, step: step} + var margin = parseInt(can.Action(html.MARGIN)), step = parseFloat((width-2*margin) / can._msg.Length()) + return {height: height, width: width, margin: margin, step: step} }, layout: function(can) { can.onmotion.toggle(can, can._option, !can.user.isMobile || !can.isFullMode()) + can.Conf(ice.VIEW) && can.Action(ice.VIEW, can.Conf(ice.VIEW)) can.onaction[can.Action(ice.VIEW)]({}, can) }, transform: function(can, target) { target.Value("transform", "translate(0, "+parseInt(can.ConfHeight())+") scale(1, -1)") }, }, [""]) -Volcanos(chat.ONACTION, {help: "组件菜单", list: [ +Volcanos(chat.ONACTION, {list: [ [ice.VIEW, "趋势图", "柱状图", "折线图", "数据源"], [html.HEIGHT, ice.AUTO, 100, 200, 400, 600, 800], - ["space", 10, 20, 50, 100], + [html.MARGIN, 10, 20, 50, 100], [html.SPEED, 10, 20, 50, 100], ], - "折线图": function(event, can) { var args = can.onimport._layout(can) + "趋势图": function(event, can) { var args = can.onimport._layout(can) + function scale(y) { return (y - can.min)/(can.max - can.min)*(args.height-2*args.margin) } + function order(index, x, y) { return {x: args.margin+args.step*index+x, y: args.height-args.margin-scale(y)} } + var black = can.onimport.group(can, cli.BLACK, kit.Dict(svg.STROKE, cli.BLACK, svg.FILL, cli.BLACK)) + var white = can.onimport.group(can, cli.WHITE, kit.Dict(svg.STROKE, cli.WHITE, svg.FILL, cli.WHITE)) + can.core.Next(can.onimport._sum(can), function(item, next, index) { can.core.Timer(parseInt(can.Action(html.SPEED)), next), can.Status(item) + can.onimport.draw(can, {shape: svg.LINE, points: [ + order(index, args.step/2, item.min), order(index, args.step/2, item.max), + ]}, item.begin < item.close? white: black) + + can.onimport.draw(can, {shape: svg.RECT, points: [ + order(index, args.step/4, item.close), order(index, args.step/4*3, item.begin), + ], style: {rx: 0, ry:0}, _init: function(target) { + can.core.ItemCB(can.ondetail, function(key, cb) { target[key] = function(event) { cb(event, can, item) } }) + }}, item.begin < item.close? white: black) + }) + }, + "折线图": function(event, can) { var args = can.onimport._layout(can); args.step = parseFloat((args.width-2*args.margin) / (can._msg.Length()-1)) var black = can.onimport.group(can, cli.BLACK, kit.Dict(svg.STROKE, cli.BLACK, svg.FILL, cli.BLACK)) var white = can.onimport.group(can, cli.WHITE, kit.Dict(svg.STROKE, cli.WHITE, svg.FILL, cli.WHITE)) can.onimport.transform(can, black), can.onimport.transform(can, white) - can.core.List(can.list, function(list) { - var max = list[0], min = list[0], step = (can.ConfWidth()-2*args.space)/(list.length-1) - for (var i = 1; i < list.length; i += 1) { if (list[i] > max) { max = list[i] } if (list[i] < min) { min = list[i] } } - function scale(y) { return (y - min)/(max - min)*(args.height-2*args.space)+args.space } - function order(i) { return i*step+args.space } - for (var i = 1; i < list.length; i += 1) { - can.onimport.draw({}, can, {shape: svg.LINE, point: [{x: order(i-1), y: scale(list[i-1])}, {x: order(i), y: scale(list[i])}]}, white) + can.core.List(can.base.Obj(can.Conf(mdb.FIELD), can._msg.append), function(field) { var max = can.data[0][field], min = can.data[0][field] + for (var i = 1; i < can.data.length; i += 1) { var value = can.data[i][field]; if (value > max) { max = value } if (value < min) { min = value } } + function order(i) { return i*args.step+args.margin } function scale(y) { return (y - min)/(max - min)*(args.height-2*args.margin)+args.margin } + for (var i = 1; i < can.data.length; i += 1) { + can.onimport.draw(can, {shape: svg.LINE, points: [{x: order(i-1), y: scale(can.data[i-1][field])}, {x: order(i), y: scale(can.data[i][field])}]}, white) } }) }, - "趋势图": function(event, can) { var args = can.onimport._layout(can) - function scale(y) { return (y - can.min)/(can.max - can.min)*(args.height-2*args.space) } - function order(index, x, y) { return {x: args.space+args.step*index+x, y: args.height-args.space-scale(y)} } - + "柱状图": function(event, can) { var args = can.onimport._layout(can) var black = can.onimport.group(can, cli.BLACK, kit.Dict(svg.STROKE, cli.BLACK, svg.FILL, cli.BLACK)) var white = can.onimport.group(can, cli.WHITE, kit.Dict(svg.STROKE, cli.WHITE, svg.FILL, cli.WHITE)) - - can.core.Next(can.list, function(line, next, index) { can.Status(line) - can.onimport.draw({}, can, {shape: svg.LINE, point: [ - order(index, args.step/2, line.min), order(index, args.step/2, line.max), - ]}, line.begin < line.close? white: black) - - can.onimport.draw({}, can, {shape: svg.RECT, point: [ - order(index, args.step/4, line.close), order(index, args.step/4*3, line.begin), - ], style: {rx: 0, ry:0}, _init: function(view) { - can.core.ItemCB(can.ondetail, function(key, cb) { view[key] = function(event) { cb(event, can, line) } }) - }}, line.begin < line.close? white: black) - - can.core.Timer(parseInt(can.Action(html.SPEED)), next) - }) - }, - "柱状图": function(event, can) { var args = can.onimport._layout(can) - var max = {}, min = {}; can.core.List(can._msg.append, function(key, which) { - can.core.List(can.data, function(value, index) { - var v = parseInt(value[key])||0; if (index == 0) { - return max[key] = v, min[key] = v - } - if (v > max[key]) { max[key] = v } - if (v < min[key]) { min[key] = v } + can.onimport.transform(can, black), can.onimport.transform(can, white) + can.core.List(can.base.Obj(can.Conf(mdb.FIELD), can._msg.append), function(field) { var max = can.data[0][field], min = can.data[0][field] + for (var i = 1; i < can.data.length; i += 1) { var value = can.data[i][field]; if (value > max) { max = value } if (value < min) { min = value } } + function order(i) { return i*args.step+args.margin } function scale(y) { return (y - min)/(max - min)*(args.height-2*args.margin)+args.margin } + can.core.Next(can.data, function(item, next, i) { can.core.Timer(parseInt(can.Action(html.SPEED)), next) + can.onimport.draw(can, {shape: svg.RECT, style: {rx: 0, ry: 0}, points: [{x: order(i)+args.step/8.0, y: args.margin}, {x: order(i)+args.step/8.0*7, y: scale(item[field])}], _init: function(target) { + can.core.ItemCB(can.ondetail, function(key, cb) { target[key] = function(event) { cb(event, can, item) } }) + }}, white) }) }) - - function scale(key, y) { return (y - min[key])/(max[key] - min[key])*(args.height-2*args.space) } - - var width = args.step/can._msg.append.length, which = 0 - can.core.List(can._msg.append, function(key, which) { if (max[key] == min[key]) { return } - can.core.Next(can.data, function(line, next, index) { if (parseInt(line[key]) == 0) { return } - can.onimport.draw({}, can, {shape: svg.RECT, point: [ - {x: args.space+args.step*index+width*which+2, y: args.height-args.space-scale(key, parseInt(line[key]))}, - {x: args.space+args.step*index+width*(which+1)+2, y: args.height-args.space}, - ], style: kit.Dict(svg.STROKE_WIDTH, 1, svg.STROKE, cli.WHITE, svg.FILL, cli.WHITE, svg.RX, 0, svg.RY, 0), _init: function(view) { - can.core.ItemCB(can.ondetail, function(key, cb) { view[key] = function(event) { cb(event, can, line) } }) - }}), can.core.Timer(parseInt(can.Action(html.SPEED)), next) - }), which++ - }) - }, - "数据源": function(event, can) { - can.onmotion.clear(can, can._output) - can.onappend.table(can, can._msg, null, can._output) }, + "数据源": function(event, can) { can.onmotion.clear(can), can.onappend.table(can, can._msg) }, height: function(event, can) { can.onimport.layout(can) }, - space: function(event, can) { can.onimport.layout(can) }, + margin: function(event, can) { can.onimport.layout(can) }, speed: function(event, can) { can.onimport.layout(can) }, }) -Volcanos(chat.ONDETAIL, {help: "用户交互", - onmouseenter: function(event, can, line) { can.Status(line) }, +Volcanos(chat.ONDETAIL, { + onmouseenter: function(event, can, item) { can.Status(item) }, }) -Volcanos(chat.ONEXPORT, {help: "导出数据", list: ["from", "commit", "total", "max", "date", "text", "add", "del"], +Volcanos(chat.ONEXPORT, {list: ["from", "commit", "total", "max", "date", "text", "add", "del"], height: function(can) { var height = can.Action(html.HEIGHT) - if (height == ice.AUTO) { height = can.ConfHeight() - // can.isFullMode() || (height = can.base.Max(can.ConfHeight(), 600)) - } - if (height < 200) { height = 200 } + if (height == ice.AUTO) { height = can.ConfHeight() } if (height < 200) { height = 200 } return parseInt(height||can.page.height()/2) }, }) diff --git a/proto.js b/proto.js index d1702cb5..c6e580bb 100644 --- a/proto.js +++ b/proto.js @@ -96,7 +96,7 @@ var mdb = { var ssh = { } var nfs = { - PATH: "path", FILE: "file", LINE: "line", SIZE: "size", + PATH: "path", FILE: "file", LINE: "line", SIZE: "size", ROOT: "root", SAVE: "save", LOAD: "load", FIND: "find", GREP: "grep", TAGS: "tags", DIR: "dir", CAT: "cat", DEFS: "defs", TRASH: "trash", SCRIPT: "script", CONTENT: "content", DIR_ROOT: "dir_root", PWD: "./", HTML: "html", CSS: "css", JS: "js", GO: "go", SH: "sh", CSV: "csv", JSON: "json", @@ -197,6 +197,7 @@ var svg = { G: "g", X: "x", Y: "y", R: "r", RX: "rx", RY: "ry", CX: "cx", CY: "cy", X1: "x1", Y1: "y1", X2: "x2", Y2: "y2", PATH: "path", PATH2V: "path2v", PATH2H: "path2h", M: "M", Q: "Q", T: "T", + TEXT_LENGTH: "textLength", } var html = {PLUGIN_MARGIN: 10, ACTION_HEIGHT: 31, ACTION_MARGIN: 200, FIELDSET: "fieldset", LEGEND: "legend", OPTION: "option", ACTION: "action", OUTPUT: "output", STATUS: "status", @@ -212,7 +213,7 @@ var html = {PLUGIN_MARGIN: 10, ACTION_HEIGHT: 31, ACTION_MARGIN: 200, SPAN: "span", CODE: "code", DIV: "div", IMG: "img", VIDEO: "video", SPACE: "space", WSS: "wss", SVG: "svg", CANVAS: "canvas", IFRAME: "iframe", CHROME: "chrome", - CLASS: "class", DISPLAY: "display", BLOCK: "block", NONE: "none", HIDDEN: "hidden", TOGGLE: "toggle", + CLASS: "class", DISPLAY: "display", BLOCK: "block", NONE: "none", HIDDEN: "hidden", TOGGLE: "toggle", SIZE: "size", HEIGHT: "height", WIDTH: "width", PADDING: "padding", MARGIN: "margin", LEFT: "left", TOP: "top", RIGHT: "right", BOTTOM: "bottom", MIN_HEIGHT: "min-height", MAX_HEIGHT: "max-height", MIN_WIDTH: "min-width", MAX_WIDTH: "max-width", MARGIN_TOP: "margin-top", MARGIN_X: "margin-x", MARGIN_Y: "margin-y", BACKGROUND: "background", OPACITY: "opacity", OVERFLOW: "overflow", SCROLL: "scroll", SPEED: "speed", FLOAT: "float", CLEAR: "clear", BOTH: "both", @@ -258,6 +259,11 @@ var Volcanos = shy({iceberg: "/chat/", volcano: "/frame.js", cache: {}, pack: {} libs[i] = "/require/node_modules/"+libs[i] } can.require(libs, cb, cbs) }, + requireDraw: function(cb) { can.page.ClassList.add(can, can._fields, "draw") + can.require(["/plugin/local/wiki/draw.js", "/plugin/local/wiki/draw/path.js"], function() { + can.onmotion.clear(can), can.onimport._show(can, can._msg), cb() + }) + }, require: function(libs, cb, cbs) { if (!libs || libs.length == 0) { return typeof cb == lang.FUNCTION && setTimeout(function() { cb(can) }, 10) } if (libs[0] == undefined) { return can.require(libs.slice(1), cb, cbs) }