aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJosé Bollo <jose.bollo@iot.bzh>2017-08-27 13:35:37 +0200
committerJosé Bollo <jose.bollo@iot.bzh>2017-08-27 14:41:45 +0200
commit8ce4768e7bbb2f7fc00c885591aa20429f4c83af (patch)
tree0de9a26095e3128a47e89681d48126ee62383f8f
parentd0b0b6fe77b8ea06bac2830baa9008eefe1917ba (diff)
monitoring: improvements
- use flex/css for correct layout - handles disconnection nicely - handles initial token - fix typo in afb-api-so Change-Id: I386b98f9d6a2182029d39a373a9820478f97cfa6 Signed-off-by: José Bollo <jose.bollo@iot.bzh>
-rw-r--r--src/afb-api-so.c2
-rw-r--r--test/monitoring/AFB.js2
-rw-r--r--test/monitoring/background-iot.bzh.jpegbin0 -> 32056 bytes
-rw-r--r--test/monitoring/content-background-car-wide.pngbin1215422 -> 0 bytes
-rw-r--r--test/monitoring/monitor-base.css128
-rw-r--r--test/monitoring/monitor-demo.css58
-rw-r--r--test/monitoring/monitor-pastel.css5
-rw-r--r--test/monitoring/monitor.html51
-rw-r--r--test/monitoring/monitor.js213
9 files changed, 231 insertions, 228 deletions
diff --git a/src/afb-api-so.c b/src/afb-api-so.c
index 9d8d9c07..55e474c6 100644
--- a/src/afb-api-so.c
+++ b/src/afb-api-so.c
@@ -86,7 +86,7 @@ static int adddirs(char path[PATH_MAX], size_t end, struct afb_apiset *apiset, i
DIR *dir;
struct dirent *dent;
size_t len;
- int rc=0;
+ int rc = 0;
/* open the DIR now */
dir = opendir(path);
diff --git a/test/monitoring/AFB.js b/test/monitoring/AFB.js
index db03d9e8..30fb9826 100644
--- a/test/monitoring/AFB.js
+++ b/test/monitoring/AFB.js
@@ -21,7 +21,7 @@ if (typeof base != "object")
var initial = {
base: base.base || "api",
- token: base.token || "hello",
+ token: base.token || initialtoken || "hello",
host: base.host || window.location.host,
url: base.url || undefined
};
diff --git a/test/monitoring/background-iot.bzh.jpeg b/test/monitoring/background-iot.bzh.jpeg
new file mode 100644
index 00000000..2edcfac8
--- /dev/null
+++ b/test/monitoring/background-iot.bzh.jpeg
Binary files differ
diff --git a/test/monitoring/content-background-car-wide.png b/test/monitoring/content-background-car-wide.png
deleted file mode 100644
index 073d1b9d..00000000
--- a/test/monitoring/content-background-car-wide.png
+++ /dev/null
Binary files differ
diff --git a/test/monitoring/monitor-base.css b/test/monitoring/monitor-base.css
index 1318c914..f2de1d92 100644
--- a/test/monitoring/monitor-base.css
+++ b/test/monitoring/monitor-base.css
@@ -19,108 +19,67 @@
body {
position: fixed;
top: 0px;
- bottom: 0px;
left: 0px;
right: 0px;
+ bottom: 0px;
}
-body.on #params, body.off #main { display: none; }
+body.on #params, body.on #connect, body.off #disconnect { display: none; }
/*******************************************************************/
-/* head */
-#head {
- position: relative;
+/* utilities */
+.-flex-h {
+ display: flex;
+ flex-flow: row nowrap;
}
-
-#logo {
- float: left;
+.-flex-v {
+ display: flex;
+ flex-flow: column nowrap;
}
-
-#connected {
- float: right;
+.-flex-v > .-flex-fill {
+ height: 100%;
}
-
-/*******************************************************************/
-/* connection area */
-
-/*******************************************************************/
-/* main area */
-
-#work {
- position: relative;
- top: 0px;
- bottom: 0px;
- left: 0px;
- right: 0px;
+.-flex-h > .-flex-fill {
+ width: 100%;
}
-
-#main {
+.-box-out {
position: relative;
- top: 0px;
- bottom: 0px;
- left: 0px;
- right: 0px;
}
-
-.fillfix {
- width: 100%;
- height: 100%;
-}
-
-#controls {
+.-box-in {
position: absolute;
- width: 250px;
left: 0px;
- top: 0px;
- bottom: 0px;
- overflow: auto;
- margin-bottom: 75px;
-}
-
-#logmsg-box {
- position: absolute;
- width: 250px;
right: 0px;
top: 0px;
bottom: 0px;
-/*
- font-size: smaller;
overflow: auto;
- margin-bottom: 75px;
-*/
}
-#logmsgs-inner-box {
+/*******************************************************************/
+/* head */
+#head {
position: relative;
- left: 0px;
- right: 0px;
- top: 0px;
- bottom: 0px;
}
-#logmsgs {
- position: absolute;
- left: 0px;
- right: 0px;
- top: 0px;
- bottom: 0px;
- font-size: smaller;
- overflow: auto;
- margin-bottom: 380px;
+#logo {
+ float: left;
}
-
-#trace-events {
- overflow: auto;
- position: absolute;
- right: 250px;
- left: 250px;
- top: 0px;
- bottom: 0px;
- margin-bottom: 75px;
+#connected {
+ float: right;
}
/*******************************************************************/
+/* connection area */
+
+/*******************************************************************/
+/* main area */
+
+#controls, #menu { flex: 1; }
+#middle { flex: 3; }
+#controls { min-width: 300px; }
+#menu { min-width: 200px; }
+
+/*******************************************************************/
/* setting for apis */
/*******************************************************************/
@@ -140,6 +99,8 @@ body.on #params, body.off #main { display: none; }
/*******************************************************************/
/* display of logmsg */
+#experts.closed ~ #expert-pane { visibility: hidden; }
+
/*******************************************************************/
/* close box */
.close {
@@ -167,7 +128,7 @@ body.on #params, body.off #main { display: none; }
}
.traceevent .time {
- float: left;
+ float: right;
}
.traceevent.closed:hover {
@@ -183,9 +144,26 @@ body.on #params, body.off #main { display: none; }
.traceevent.closed:hover .content {
display: block;
background: inherit;
+ overflow: hidden;
left: 40%;
max-width: 55%;
position: absolute;
+ animation-name: display-block;
+ animation-duration: 2s;
+ -webkit-animation-name: display-block;
+ -webkit-animation-duration: 2s;
+}
+
+@-webkit-keyframes display-block {
+ from { opacity: 0; }
+ 50% { opacity: 0; }
+ to { opacity: 1; }
+}
+
+@keyframes display-block {
+ from { opacity: 0; }
+ 50% { opacity: 0; }
+ to { opacity: 1; }
}
.traceevent .content {
diff --git a/test/monitoring/monitor-demo.css b/test/monitoring/monitor-demo.css
index f1455b79..87cf0a0e 100644
--- a/test/monitoring/monitor-demo.css
+++ b/test/monitoring/monitor-demo.css
@@ -16,21 +16,19 @@
*/
/*******************************************************************/
/* top */
-body {
+html {
margin: 2px;
- background-color: darkslategray;
- background-image: url(content-background-car-wide.png);
- background-position: center;
- background-size: cover;
+ background: darkslategray url(background-iot.bzh.jpeg) center/cover;
color: lightsteelblue;
font-family: "Lucida Console", Monaco, monospace;
+ min-height: 100%;
}
/*******************************************************************/
/* head */
#logo {
- background: url(iot-bzh-logo-small.png) center/contain no-repeat;
+ background: url(iot-bzh-logo-small.png) center/50% no-repeat;
height: 60px;
width: 200px;
}
@@ -64,11 +62,6 @@ body {
border-radius: 0px 50px;
}
-#connect {
- float: right;
- margin: 20px;
-}
-
/*******************************************************************/
/* main area */
@@ -206,6 +199,8 @@ body {
height: 10;
background-image: url(cross.png);
background-size: contain;
+ background-repeat: no-repeat;
+ background-position: center;
}
/*******************************************************************/
@@ -235,41 +230,42 @@ body {
}
.separator {
- margin: 15px 100px;
+ margin: 25px 100px;
padding: 0px;
height: 2px;
- background-color: lightsteelblue;
+ background-color: #ee3;
}
.traceevent.opened {
background-color: rgba(0,0,0,0.6);
}
-/*
-.traceevent.request, .trace-box.request { background: #ffd: }
-.traceevent.daemon, .trace-box.daemon { background: #fdf; }
-.traceevent.service, .trace-box.service { background: #ddf; }
-.traceevent.event, .trace-box.event { background: #dfd; }
-*/
.traceevent.request, .trace-box.request { color: lightsteelblue; }
-.traceevent.daemon, .trace-box.daemon { color: #66f; }
+.traceevent.daemon, .trace-box.daemon { color: #5af; }
.traceevent.service, .trace-box.service { color: #0f0; }
-.traceevent.event, .trace-box.event { color: #f0d; }
+.traceevent.event, .trace-box.event { color: #f52; }
.traceevent.closed {
max-height: 16px;
}
+.traceevent .close {
+ height: 16px;
+ margin: -1px 0px 0px 0px;
+ padding: 1px;
+ background-color: grey;
+}
+
.traceevent .time {
height: 16px;
- margin: -2px 8px 2px -2px;
+ margin: -1px 0px 0px 8px;
padding: 1px 3px;
- background: black;
- color: white;
- font-weight: bolder;
+ background-color: grey;
+ color: black;
}
.traceevent .tag {
+ margin-left: 10px;
font-weight: bolder;
}
@@ -280,10 +276,10 @@ body {
.traceevent.closed:hover .content {
margin: 5px;
padding: 5px;
- border: solid 1px;
- border-radius: 0px 5px 5px 5px;
+ border: solid 4px;
+ border-radius: 0px 20px 20px 20px;
box-shadow: 10px 10px #dd8;
- background-color: rgba(0,0,0,0.8);
+ background-color: rgba(0,0,0,0.6);
}
.traceevent table.object tr td:nth-child(1) {
@@ -293,11 +289,9 @@ body {
}
.traceevent table.object tr td:nth-child(2) {
-/*
- border: solid 1px black;
-*/
background-color: rgba(0,0,0,0.4);
padding: 0px 4px;
+ overflow: auto;
}
.traceevent table {
@@ -312,7 +306,7 @@ body {
a:link { color: cyan; }
a:link:hover, a:active { color: white; }
-a:visited { color: lightsteelblue; }
+a:visited { color: lightgrey; }
/*******************************************************************/
/* json format */
diff --git a/test/monitoring/monitor-pastel.css b/test/monitoring/monitor-pastel.css
index 21bccd7b..0264bf52 100644
--- a/test/monitoring/monitor-pastel.css
+++ b/test/monitoring/monitor-pastel.css
@@ -57,11 +57,6 @@ body {
border-radius: 0px 50px;
}
-#connect {
- float: right;
- margin: 20px;
-}
-
/*******************************************************************/
/* main area */
diff --git a/test/monitoring/monitor.html b/test/monitoring/monitor.html
index 9e35bc76..f22a62b2 100644
--- a/test/monitoring/monitor.html
+++ b/test/monitoring/monitor.html
@@ -23,22 +23,15 @@
<script type="text/javascript" src="AFB.js"></script>
<script type="text/javascript" src="monitor.js"></script>
-<body id="root" class="on" onload="init();">
+<body id="root" class="-flex-v on" onload="init();">
<div id="head" class="clearfix">
<div id="logo"></div>
<div id="connected">Not Connected</div>
<div id="title">Monitoring</div>
</div>
- <div id="work">
- <div id="params" class="clearfix">
- <div id="connect" class="x-button">connect</div>
- <div>host: <input type="text" id="param-host" size="50" value="localhost"></input></div>
- <div>port: <input type="text" id="param-port" size="10" value="1234"></input></div>
- <div>token: <input type="text" id="param-token" size="33" value="hello"></input></div>
- </div>
- <div id="main">
- <div class="fillfix"></div>
- <div id="controls">
+ <div id="work" class="-flex-fill -flex-h">
+ <div id="controls" class="-box-out">
+ <div class="-box-in">
<div id="all" class="api opened" data-api="*">
<div class="opclo"></div>
<div class="name">{ALL}</div>
@@ -58,27 +51,37 @@
<div id="apis">
</div>
</div>
- <div id="logmsg-box">
+ </div>
+ <div id="middle" class="-flex-v">
+ <div id="params" class="clearfix">
+ <div>host: <input type="text" id="param-host" size="50" value="localhost"></input></div>
+ <div>port: <input type="text" id="param-port" size="10" value="1234"></input></div>
+ <div>token: <input type="text" id="param-token" size="33" value="hello"></input></div>
+ </div>
+ <div class="-flex-fill -box-out">
+ <div id="trace-events" class="-box-in">
+ </div>
+ </div>
+ </div>
+
+ <div id="menu" class="-flex-v">
+ <div id="connect" class="x-button">Connect</div>
<div id="disconnect" class="x-button">Disconnect</div>
<div id="autoscroll" class="x-button">Stop scroll</div>
<div id="addsep" class="x-button">Add separator</div>
<div id="droptracevts" class="x-button">Clear traces</div>
- <div id="expert-pane" class="closed">
+ <div id="experts" class="x-button closed">
<div class="opclo"></div>
- <div class="expert">{EXPERT}</div>
- <div class="closedoff">
- <div id="stopmsgs" class="x-button">Stop logs</div>
- <div id="dropmsgs" class="x-button">Clear logs</div>
- <div id="logmsgs-inner-box">
- <div class="fillfix"></div>
- <div id="logmsgs"></div>
- </div>
+ {EXPERTS}
+ </div>
+ <div id="expert-pane" class="-flex-fill -flex-v">
+ <div id="stopmsgs" class="x-button">Stop logs</div>
+ <div id="dropmsgs" class="x-button">Clear logs</div>
+ <div class="-flex-fill -box-out">
+ <div id="logmsgs" class="-box-in"></div>
</div>
</div>
</div>
- <div id="trace-events">
- </div>
- </div>
</div>
<!-- template for APIS -->
diff --git a/test/monitoring/monitor.js b/test/monitoring/monitor.js
index 2c532569..ecf10962 100644
--- a/test/monitoring/monitor.js
+++ b/test/monitoring/monitor.js
@@ -47,34 +47,38 @@ _.templateSettings = { interpolate: /\{\{(.+?)\}\}/g };
function untrace_all() {
do_call("monitor/trace", {drop: true});
- for_all_nodes(null, ".trace-item input[type=radio]", function(n){n.checked = n.value == "no";});
}
-function disconnect() {
- untrace_all();
- apis = {};
- apis_node.innerHTML = "";
- root_node.className = "off";
+function disconnect(status) {
+ class_toggle(root_node, { on: "off" }, "off");
connected_node.innerHTML = "Connection Closed";
- connected_node.className = "ok";
- ws && ws.close();
- afb = null;
+ connected_node.className = status;
+ if (ws) {
+ untrace_all();
+ ws.onclose = ws.onabort = null;
+ ws.close();
+ }
ws = null;
+ if (afb)
+ at("param-token").value = afb.context.token;
+ afb = null;
}
-function connect(args) {
- drop_all_trace_events();
- drop_all_logmsgs();
- ws && ws.close();
- afb = new AFB(args);
- ws = new afb.ws(onopen, onabort);
+function on_disconnect() {
+ disconnect("ok");
}
-function on_connect(evt) {
- connect({
+function connect() {
+ ws && ws.close();
+ afb = new AFB({
host: at("param-host").value + ":" + at("param-port").value,
token: at("param-token").value
});
+ ws = new afb.ws(onopen, onabort);
+}
+
+function on_connect(evt) {
+ connect();
}
function init() {
@@ -103,7 +107,7 @@ function init() {
for_all_nodes(root_node, ".opclo ~ :not(.closedoff)", function(n){n.onclick = on_toggle_opclo});
for_all_nodes(root_node, ".verbosity select", function(n){n.onchange = set_verbosity});
for_all_nodes(root_node, ".trace-item input", function(n){n.onchange = on_trace_change});
- at("disconnect").onclick = disconnect;
+ at("disconnect").onclick = on_disconnect;
at("connect").onclick = on_connect;
at("droptracevts").onclick = drop_all_trace_events;
at("dropmsgs").onclick = drop_all_logmsgs;
@@ -113,6 +117,14 @@ function init() {
at("autoscroll").onclick = toggle_autoscroll;
start_autoscroll(true);
at("addsep").onclick = add_separator;
+ at("experts").onclick = toggle_experts;
+
+ at("param-host").value = document.location.hostname;
+ at("param-port").value = document.location.port;
+ var args = new URLSearchParams(document.location.search.substring(1));
+ at("param-token").value = args.get("x-afb-token") || args.get("token") || "hello";
+
+ document.onbeforeunload = on_disconnect;
connect();
}
@@ -137,17 +149,18 @@ function plug(target, sel, node) {
}
function onopen() {
- root_node.className = "on";
+ class_toggle(root_node, { off: "on" }, "on");
connected_node.innerHTML = "Connected " + ws.url;
connected_node.className = "ok";
ws.onevent("*", gotevent);
ws.onclose = onabort;
+ untrace_all();
+ for_all_nodes(all_node, ".trace-box", update_trace_box);
do_call("monitor/get", {apis:true,verbosity:true}, on_got_apis, on_error_apis);
}
+
function onabort() {
- root_node.className = "off";
- connected_node.innerHTML = "Connection Closed";
- connected_node.className = "error";
+ disconnect("error");
}
function start_autoscroll(val) {
@@ -163,6 +176,12 @@ function add_separator() {
trace_events_node.append(x);
if (autoscroll)
x.scrollIntoView();
+ if (msgs) {
+ x = document.importNode(t_separator, true);
+ logmsgs_node.append(x);
+ if (autoscroll)
+ x.scrollIntoView();
+ }
}
function start_logmsgs(val) {
@@ -249,10 +268,14 @@ function set_verbosity(evt) {
/* show all apis */
function on_got_apis(obj) {
inhibit = true;
+ var saved_apis = apis;
+ apis = {};
+ apis_node.innerHTML = "";
_.each(obj.response.apis, function(api_desc, api_name){
if (api_name == "monitor") return;
- var api = apis[api_name];
+ var api = saved_apis[api_name];
if (!api) {
+ /* create the node */
api = {
node: document.importNode(t_api, true),
verbs: {},
@@ -261,42 +284,45 @@ function on_got_apis(obj) {
api.node.API = api;
api.node.dataset.api = api_name;
api.vnode = get(".verbs", api.node);
- apis[api_name] = api;
get(".name", api.node).textContent = api_name;
- get(".desc", api.node).textContent = api_desc.info.description || "";
- for_all_nodes(api.node, ".opclo", function(n){n.onclick = on_toggle_opclo});
- for_all_nodes(api.node, ".opclo ~ :not(.closedoff)", function(n){n.onclick = on_toggle_opclo});
- for_all_nodes(api.node, ".trace-item input", function(n){n.onchange = on_trace_change});
- apis_node.append(api.node);
- _.each(api_desc.paths, function(verb_desc, path_name){
- var verb_name = path_name.substring(1);
- var verb = api.verbs[verb_name];
- if (!verb) {
- verb = {
- node: document.importNode(t_verb, true),
- name: verb_name,
- api: api
- };
- verb.node.VERB = verb;
- verb.node.dataset.verb = verb_name;
- api.verbs[verb_name] = verb;
- get(".name", verb.node).textContent = verb_name;
- var g = verb_desc.get ||{};
- var r = g["responses"] || {};
- var t = r["200"] || {};
- var d = t.description || "";
- get(".desc", verb.node).textContent = d;
- if (show_perms) {
- var p = g["x-permissions"] || "";
- get(".perm", verb.node).textContent = p ? JSON.stringify(p, null, 1) : "";
- }
- api.vnode.append(verb.node);
- }
- });
var s = get(".verbosity select", api.node);
s.API = api;
s.onchange = set_verbosity;
+ for_all_nodes(api.node, ".opclo", function(n){n.onclick = on_toggle_opclo});
+ for_all_nodes(api.node, ".opclo ~ :not(.closedoff)", function(n){n.onclick = on_toggle_opclo});
+ for_all_nodes(api.node, ".trace-item input", function(n){n.onchange = on_trace_change});
+ } else {
+ /* reactivate the expected traces */
+ for_all_nodes(api.node, ".trace-box", update_trace_box);
}
+ apis[api_name] = api;
+ get(".desc", api.node).textContent = api_desc.info.description || "";
+ _.each(api_desc.paths, function(verb_desc, path_name){
+ var verb_name = path_name.substring(1);
+ var verb = api.verbs[verb_name];
+ if (!verb) {
+ verb = {
+ node: document.importNode(t_verb, true),
+ name: verb_name,
+ api: api
+ };
+ verb.node.VERB = verb;
+ verb.node.dataset.verb = verb_name;
+ api.verbs[verb_name] = verb;
+ get(".name", verb.node).textContent = verb_name;
+ var g = verb_desc.get ||{};
+ var r = g["responses"] || {};
+ var t = r["200"] || {};
+ var d = t.description || "";
+ get(".desc", verb.node).textContent = d;
+ if (show_perms) {
+ var p = g["x-permissions"] || "";
+ get(".perm", verb.node).textContent = p ? JSON.stringify(p, null, 1) : "";
+ }
+ api.vnode.append(verb.node);
+ }
+ });
+ apis_node.append(api.node);
});
inhibit = false;
on_got_verbosities(obj);
@@ -306,37 +332,42 @@ function on_toggle_opclo(evt) {
toggle_opened_closed(evt.target.parentElement);
}
-function on_trace_change(evt) {
- var obj = evt.target;
- var tra = obj.parentElement;
- while (tra && !tra.dataset.trace)
- tra = tra.parentElement;
- var api = tra;
+function toggle_experts(evt) {
+ toggle_opened_closed(evt.target);
+}
+
+function update_trace_box(node) {
+ set_trace_box(node, false);
+}
+
+function set_trace_box(node, clear) {
+ var api = node;
while (api && !api.dataset.api)
api = api.parentElement;
- var tag = api.dataset.api + "/" + tra.dataset.trace;
- if (tra) {
- var drop = false;
- for_all_nodes(tra, "input", function(n){
- if (n.checked) {
- n.checked = false;
- if (n != obj && n.value != "no")
- drop = true;
- }
- });
- if (drop)
- do_call("monitor/trace", {drop: {tag: tag}});
- obj.checked = true;
- if (obj.value != "no") {
- var spec = {tag: tag, name: "trace"};
- spec[tra.dataset.trace] = obj.value;
- if (api.dataset.api != "*")
- spec.api = api.dataset.api;
- do_call("monitor/trace", {add: spec});
- }
+ var tag = api.dataset.api + "/" + node.dataset.trace;
+ var value = false;
+ for_all_nodes(node, "input", function(n){ if (n.checked) value = n.value; });
+ if (clear)
+ do_call("monitor/trace", {drop: {tag: tag}});
+ if (value != "no") {
+ var spec = {tag: tag, name: "trace"};
+ spec[node.dataset.trace] = value;
+ if (api.dataset.api != "*")
+ spec.api = api.dataset.api;
+ do_call("monitor/trace", {add: spec});
}
}
+function on_trace_change(evt) {
+ var obj = evt.target;
+ var box = obj.parentElement;
+ while (box && !box.dataset.trace)
+ box = box.parentElement;
+ for_all_nodes(box, "input", function(n){n.checked = false;});
+ obj.checked = true;
+ set_trace_box(box, true);
+}
+
function makecontent(node, deep, val) {
if (--deep > 0) {
if (_.isObject(val)) {
@@ -348,7 +379,7 @@ function makecontent(node, deep, val) {
return;
}
}
- node.innerHTML = obj2html(val);
+ node.innerHTML = '<pre>' + obj2html(val) + '</pre>';
}
function makearritem(tbl, deep, val) {
@@ -424,23 +455,25 @@ function gottraceevent(obj) {
x.scrollIntoView();
}
-function toggle_opened_closed(node, defval) {
+function class_toggle(node, assoc, defval) {
var matched = false;
var cs = node.className.split(" ").map(
function(x){
- if (!matched) {
- switch(x) {
- case "closed": matched = true; return "opened";
- case "opened": matched = true; return "closed";
- }
+ if (!matched && (x in assoc)) {
+ matched = true;
+ return assoc[x];
}
- return x;
+ return x == defval ? "" : x;
}).join(" ");
- if (!matched)
- cs = cs + " " + (defval || "closed");
+ if (!matched && defval)
+ cs = cs + " " + defval;
node.className = cs;
}
+function toggle_opened_closed(node, defval) {
+ class_toggle(node, { closed: "opened", opened: "closed" }, defval);
+}
+
function on_toggle_traceevent(evt) {
if (getSelection() != "") return;
var node = evt.target;