webmachine 0.4.2 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +70 -7
- data/Rakefile +19 -0
- data/examples/debugger.rb +32 -0
- data/examples/webrick.rb +6 -2
- data/lib/webmachine.rb +2 -0
- data/lib/webmachine/adapters/rack.rb +16 -3
- data/lib/webmachine/adapters/webrick.rb +7 -1
- data/lib/webmachine/application.rb +10 -10
- data/lib/webmachine/cookie.rb +168 -0
- data/lib/webmachine/decision/conneg.rb +1 -1
- data/lib/webmachine/decision/flow.rb +1 -1
- data/lib/webmachine/decision/fsm.rb +19 -12
- data/lib/webmachine/decision/helpers.rb +25 -1
- data/lib/webmachine/dispatcher.rb +34 -5
- data/lib/webmachine/dispatcher/route.rb +2 -0
- data/lib/webmachine/media_type.rb +3 -3
- data/lib/webmachine/request.rb +11 -0
- data/lib/webmachine/resource.rb +3 -1
- data/lib/webmachine/resource/authentication.rb +1 -1
- data/lib/webmachine/resource/callbacks.rb +16 -0
- data/lib/webmachine/resource/tracing.rb +20 -0
- data/lib/webmachine/response.rb +38 -8
- data/lib/webmachine/trace.rb +74 -0
- data/lib/webmachine/trace/fsm.rb +60 -0
- data/lib/webmachine/trace/pstore_trace_store.rb +39 -0
- data/lib/webmachine/trace/resource_proxy.rb +107 -0
- data/lib/webmachine/trace/static/http-headers-status-v3.png +0 -0
- data/lib/webmachine/trace/static/trace.erb +54 -0
- data/lib/webmachine/trace/static/tracelist.erb +14 -0
- data/lib/webmachine/trace/static/wmtrace.css +123 -0
- data/lib/webmachine/trace/static/wmtrace.js +725 -0
- data/lib/webmachine/trace/trace_resource.rb +129 -0
- data/lib/webmachine/version.rb +1 -1
- data/spec/spec_helper.rb +19 -0
- data/spec/webmachine/adapters/rack_spec.rb +77 -41
- data/spec/webmachine/configuration_spec.rb +1 -1
- data/spec/webmachine/cookie_spec.rb +99 -0
- data/spec/webmachine/decision/conneg_spec.rb +9 -8
- data/spec/webmachine/decision/flow_spec.rb +52 -4
- data/spec/webmachine/decision/helpers_spec.rb +36 -6
- data/spec/webmachine/dispatcher_spec.rb +1 -1
- data/spec/webmachine/headers_spec.rb +1 -1
- data/spec/webmachine/media_type_spec.rb +1 -1
- data/spec/webmachine/request_spec.rb +10 -0
- data/spec/webmachine/resource/authentication_spec.rb +3 -3
- data/spec/webmachine/response_spec.rb +45 -0
- data/spec/webmachine/trace/fsm_spec.rb +32 -0
- data/spec/webmachine/trace/resource_proxy_spec.rb +36 -0
- data/spec/webmachine/trace/trace_store_spec.rb +29 -0
- data/spec/webmachine/trace_spec.rb +17 -0
- data/webmachine.gemspec +2 -0
- metadata +130 -15
@@ -0,0 +1,60 @@
|
|
1
|
+
module Webmachine
|
2
|
+
module Trace
|
3
|
+
# This module is injected into {Webmachine::Decision::FSM} when
|
4
|
+
# tracing is enabled for a resource, enabling the capturing of
|
5
|
+
# traces.
|
6
|
+
module FSM
|
7
|
+
# Adds the request to the trace.
|
8
|
+
# @param [Webmachine::Request] request the request to be traced
|
9
|
+
def trace_request(request)
|
10
|
+
response.trace << {
|
11
|
+
:type => :request,
|
12
|
+
:method => request.method,
|
13
|
+
:path => request.uri.request_uri.to_s,
|
14
|
+
:headers => request.headers,
|
15
|
+
:body => request.body.to_s
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
# Adds the response to the trace.
|
20
|
+
# @param [Webmachine::Response] response the response to be traced
|
21
|
+
def trace_response(response)
|
22
|
+
response.trace << {
|
23
|
+
:type => :response,
|
24
|
+
:code => response.code.to_s,
|
25
|
+
:headers => response.headers,
|
26
|
+
:body => trace_response_body(response.body)
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
# Adds a decision to the trace.
|
31
|
+
# @param [Symbol] decision the decision being processed
|
32
|
+
def trace_decision(decision)
|
33
|
+
response.trace << {:type => :decision, :decision => decision}
|
34
|
+
end
|
35
|
+
|
36
|
+
# Overrides the default resource accessor so that incoming
|
37
|
+
# callbacks are traced.
|
38
|
+
def resource
|
39
|
+
@resource_proxy ||= ResourceProxy.new(@resource)
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
# Works around streaming encoders where possible
|
44
|
+
def trace_response_body(body)
|
45
|
+
case body
|
46
|
+
when FiberEncoder
|
47
|
+
# TODO: figure out how to properly rewind or replay the
|
48
|
+
# fiber
|
49
|
+
body.inspect
|
50
|
+
when EnumerableEncoder
|
51
|
+
body.body.join
|
52
|
+
when CallableEncoder
|
53
|
+
body.body.call.to_s
|
54
|
+
else
|
55
|
+
body.to_s
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'pstore'
|
2
|
+
|
3
|
+
module Webmachine
|
4
|
+
module Trace
|
5
|
+
# Implements a trace storage using PStore from Ruby's standard
|
6
|
+
# library. To use this trace store, specify the :pstore engine
|
7
|
+
# and a path where it can store traces:
|
8
|
+
# @example
|
9
|
+
# Webmachine::Trace.trace_store = :pstore, "/tmp/webmachine.trace"
|
10
|
+
class PStoreTraceStore
|
11
|
+
# @api private
|
12
|
+
# @param [String] path where to store traces in a PStore
|
13
|
+
def initialize(path)
|
14
|
+
@pstore = PStore.new(path)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Lists the recorded traces
|
18
|
+
# @api private
|
19
|
+
# @return [Array] a list of recorded traces
|
20
|
+
def keys
|
21
|
+
@pstore.transaction(true) { @pstore.roots }
|
22
|
+
end
|
23
|
+
|
24
|
+
# Fetches a trace from the store
|
25
|
+
# @api private
|
26
|
+
# @param [String] key the trace to fetch
|
27
|
+
# @return [Array] a raw trace
|
28
|
+
def fetch(key)
|
29
|
+
@pstore.transaction(true) { @pstore[key] }
|
30
|
+
end
|
31
|
+
|
32
|
+
# Records a trace in the store
|
33
|
+
# @api private
|
34
|
+
def []=(key, trace)
|
35
|
+
@pstore.transaction { @pstore[key] = trace }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
module Webmachine
|
2
|
+
module Trace
|
3
|
+
# This class is injected into the decision FSM as a stand-in for
|
4
|
+
# the resource when tracing is enabled. It proxies all callbacks
|
5
|
+
# to the resource so that they get logged in the trace.
|
6
|
+
class ResourceProxy
|
7
|
+
# @return [Webmachine::Resource] the wrapped resource
|
8
|
+
attr_reader :resource
|
9
|
+
|
10
|
+
# Callback methods that can return data that refers to
|
11
|
+
# user-defined callbacks that are not in the canonical set,
|
12
|
+
# including body-producing or accepting methods, encoders and
|
13
|
+
# charsetters.
|
14
|
+
CALLBACK_REFERRERS = [:content_types_accepted, :content_types_provided,
|
15
|
+
:encodings_provided, :charsets_provided]
|
16
|
+
|
17
|
+
# Creates a {ResourceProxy} that decorates the passed
|
18
|
+
# {Webmachine::Resource} such that callbacks invoked by the
|
19
|
+
# {Webmachine::Decision::FSM} will be logged in the response's
|
20
|
+
# trace.
|
21
|
+
def initialize(resource)
|
22
|
+
@resource = resource
|
23
|
+
@dynamic_callbacks = Module.new
|
24
|
+
extend @dynamic_callbacks
|
25
|
+
end
|
26
|
+
|
27
|
+
# Create wrapper methods for every exposed callback
|
28
|
+
Webmachine::Resource::Callbacks.instance_methods(false).each do |c|
|
29
|
+
define_method c do |*args|
|
30
|
+
proxy_callback c, *args
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def charset_nop(*args)
|
35
|
+
proxy_callback :charset_nop, *args
|
36
|
+
end
|
37
|
+
|
38
|
+
# Calls the resource's finish_request method and then commits
|
39
|
+
# the trace to separate storage which can be discovered by the
|
40
|
+
# debugger.
|
41
|
+
def finish_request(*args)
|
42
|
+
proxy_callback :finish_request, *args
|
43
|
+
ensure
|
44
|
+
resource.response.headers['X-Webmachine-Trace-Id'] = object_id.to_s
|
45
|
+
Trace.record(object_id.to_s, resource.response.trace)
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
# Proxy a given callback to the inner resource, decorating with traces
|
50
|
+
def proxy_callback(callback, *args)
|
51
|
+
# Log inputs and attempt
|
52
|
+
resource.response.trace << attempt(callback, args)
|
53
|
+
# Do the call
|
54
|
+
_result = resource.send(callback, *args)
|
55
|
+
add_dynamic_callback_proxies(_result) if CALLBACK_REFERRERS.include?(callback.to_sym)
|
56
|
+
resource.response.trace << result(_result)
|
57
|
+
_result
|
58
|
+
rescue => exc
|
59
|
+
exc.backtrace.reject! {|s| s.include?(__FILE__) }
|
60
|
+
resource.response.trace << exception(exc)
|
61
|
+
raise
|
62
|
+
end
|
63
|
+
|
64
|
+
# Creates a log entry for the entry to a resource callback.
|
65
|
+
def attempt(callback, args)
|
66
|
+
log = {:type => :attempt}
|
67
|
+
method = resource.method(callback)
|
68
|
+
if method.owner == ::Webmachine::Resource::Callbacks
|
69
|
+
log[:name] = "(default)##{method.name}"
|
70
|
+
else
|
71
|
+
log[:name] = "#{method.owner.name}##{method.name}"
|
72
|
+
log[:source] = method.source_location.join(":") if method.respond_to?(:source_location)
|
73
|
+
end
|
74
|
+
unless args.empty?
|
75
|
+
log[:args] = args
|
76
|
+
end
|
77
|
+
log
|
78
|
+
end
|
79
|
+
|
80
|
+
# Creates a log entry for the result of a resource callback
|
81
|
+
def result(result)
|
82
|
+
{:type => :result, :value => result}
|
83
|
+
end
|
84
|
+
|
85
|
+
# Creates a log entry for an exception that was raised from a callback
|
86
|
+
def exception(e)
|
87
|
+
{:type => :exception,
|
88
|
+
:class => e.class.name,
|
89
|
+
:backtrace => e.backtrace,
|
90
|
+
:message => e.message }
|
91
|
+
end
|
92
|
+
|
93
|
+
# Adds proxy methods for callbacks that are dynamically referred to.
|
94
|
+
def add_dynamic_callback_proxies(pairs)
|
95
|
+
pairs.to_a.each do |(_, m)|
|
96
|
+
unless respond_to?(m)
|
97
|
+
@dynamic_callbacks.module_eval do
|
98
|
+
define_method m do |*args|
|
99
|
+
proxy_callback m, *args
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
Binary file
|
@@ -0,0 +1,54 @@
|
|
1
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
2
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
3
|
+
<head>
|
4
|
+
<title>Webmachine Trace <%= name %></title>
|
5
|
+
<link rel="stylesheet" type="text/css" href="static/wmtrace.css" />
|
6
|
+
<script type="text/javascript" src="static/wmtrace.js"></script>
|
7
|
+
<script type="text/javascript">
|
8
|
+
<!--
|
9
|
+
var request = <%= treq %>;
|
10
|
+
var response = <%= tres %>;
|
11
|
+
var trace = <%= trace %>
|
12
|
+
-->
|
13
|
+
</script>
|
14
|
+
</head>
|
15
|
+
<body>
|
16
|
+
<div id="zoompanel">
|
17
|
+
<button id="zoomout">zoom out</button>
|
18
|
+
<button id="zoomin">zoom in</button>
|
19
|
+
</div>
|
20
|
+
<canvas id="v3map" width="3138" height="2184"></canvas>
|
21
|
+
<div id="sizetest"></div>
|
22
|
+
<div id="preview">
|
23
|
+
<div id="previewid"></div>
|
24
|
+
<ul id="previewcalls"></ul>
|
25
|
+
</div>
|
26
|
+
<div id="infopanel">
|
27
|
+
<div id="infocontrols">
|
28
|
+
<div id="requesttab" class="selectedtab">Q</div>
|
29
|
+
<div id="responsetab">R</div>
|
30
|
+
<div id="decisiontab">D</div>
|
31
|
+
</div>
|
32
|
+
<div id="requestdetail">
|
33
|
+
<div>
|
34
|
+
<span id="requestmethod"></span> <span id="requestpath"></span>
|
35
|
+
</div>
|
36
|
+
<table id="requestheaders" class="headers"></table>
|
37
|
+
<pre id="requestbody"></pre>
|
38
|
+
</div>
|
39
|
+
<div id="responsedetail">
|
40
|
+
<div id="responsecode"></div>
|
41
|
+
<table id="responseheaders" class="headers"></table>
|
42
|
+
<pre id="responsebody"></pre>
|
43
|
+
</div>
|
44
|
+
<div id="decisiondetail">
|
45
|
+
<div>Decision: <select id="decisionid"></select></div>
|
46
|
+
<div>Calls: <select id="decisioncalls"></select></div>
|
47
|
+
<div>Input:</div>
|
48
|
+
<pre id="callinput"></pre>
|
49
|
+
<div>Output:</div>
|
50
|
+
<pre id="calloutput"></pre>
|
51
|
+
</div>
|
52
|
+
</div>
|
53
|
+
</body>
|
54
|
+
</html>
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
2
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
3
|
+
<head>
|
4
|
+
<title>Webmachine Trace List</title>
|
5
|
+
</head>
|
6
|
+
<body>
|
7
|
+
<h1>Traces</h1>
|
8
|
+
<ul>
|
9
|
+
<% traces.each do |trace| %>
|
10
|
+
<li><a href="<%= trace %>"><%= trace %></a></li>
|
11
|
+
<% end %>
|
12
|
+
</ul>
|
13
|
+
</body>
|
14
|
+
</html>
|
@@ -0,0 +1,123 @@
|
|
1
|
+
body {
|
2
|
+
margin:0px;
|
3
|
+
padding:0px;
|
4
|
+
}
|
5
|
+
|
6
|
+
canvas#v3map {
|
7
|
+
margin-top:2em;
|
8
|
+
z-index: 1;
|
9
|
+
}
|
10
|
+
|
11
|
+
div#sizetest {
|
12
|
+
width:100%;
|
13
|
+
}
|
14
|
+
|
15
|
+
div#zoompanel {
|
16
|
+
height:2em;
|
17
|
+
position:fixed;
|
18
|
+
z-index:10;
|
19
|
+
}
|
20
|
+
|
21
|
+
div#preview {
|
22
|
+
position:absolute;
|
23
|
+
display:none;
|
24
|
+
background:#dddddd;
|
25
|
+
border:1px solid #999999;
|
26
|
+
}
|
27
|
+
|
28
|
+
div#preview ul {
|
29
|
+
padding: 0px 0px 0px 0.5em;
|
30
|
+
margin: 0px;
|
31
|
+
list-style: none;
|
32
|
+
}
|
33
|
+
|
34
|
+
div#infopanel {
|
35
|
+
z-index:20;
|
36
|
+
background:#dddddd;
|
37
|
+
position:fixed;
|
38
|
+
top:0px;
|
39
|
+
right:0px;
|
40
|
+
bottom:0px;
|
41
|
+
left:75%;
|
42
|
+
min-width:30em;
|
43
|
+
padding:5px;
|
44
|
+
}
|
45
|
+
|
46
|
+
div#infocontrols {
|
47
|
+
position:absolute;
|
48
|
+
top:0px;
|
49
|
+
bottom:0px;
|
50
|
+
left:-5px;
|
51
|
+
width:5px;
|
52
|
+
background:#999999;
|
53
|
+
cursor:ew-resize;
|
54
|
+
}
|
55
|
+
|
56
|
+
div#infocontrols div {
|
57
|
+
position:absolute;
|
58
|
+
left:-15px;
|
59
|
+
width:20px;
|
60
|
+
height:49px;
|
61
|
+
background:#999999;
|
62
|
+
cursor:pointer;
|
63
|
+
}
|
64
|
+
|
65
|
+
div#infocontrols div.selectedtab {
|
66
|
+
background:#dddddd;
|
67
|
+
border-top: 1px solid #999999;
|
68
|
+
border-left: 1px solid #999999;
|
69
|
+
border-bottom: 1px solid #999999;
|
70
|
+
}
|
71
|
+
|
72
|
+
div#requesttab {
|
73
|
+
top:2px;
|
74
|
+
}
|
75
|
+
|
76
|
+
div#responsetab {
|
77
|
+
top:54px;
|
78
|
+
}
|
79
|
+
|
80
|
+
div#decisiontab {
|
81
|
+
top:106px;
|
82
|
+
}
|
83
|
+
|
84
|
+
div#requestdetail, div#responsedetail, div#decisiondetail {
|
85
|
+
height:100%;
|
86
|
+
}
|
87
|
+
|
88
|
+
div#responsedetail, div#decisiondetail {
|
89
|
+
display:none;
|
90
|
+
}
|
91
|
+
|
92
|
+
div#infopanel ul {
|
93
|
+
list-style:none;
|
94
|
+
padding-left:0px;
|
95
|
+
height:5em;
|
96
|
+
overflow-y:scroll;
|
97
|
+
}
|
98
|
+
|
99
|
+
pre {
|
100
|
+
height:40%;
|
101
|
+
overflow: auto;
|
102
|
+
}
|
103
|
+
|
104
|
+
table.headers {
|
105
|
+
border-collapse: collapse;
|
106
|
+
margin: 10px 20px 10px 0;
|
107
|
+
}
|
108
|
+
|
109
|
+
table.headers th {
|
110
|
+
text-align: right ! important;
|
111
|
+
text-transform: capitalize;
|
112
|
+
white-space: nowrap;
|
113
|
+
vertical-align: top;
|
114
|
+
}
|
115
|
+
|
116
|
+
table.headers td, table.headers th {
|
117
|
+
padding: 5px 3px;
|
118
|
+
}
|
119
|
+
|
120
|
+
div#responsebody, div#requestbody {
|
121
|
+
height:70%;
|
122
|
+
overflow-y:scroll;
|
123
|
+
}
|
@@ -0,0 +1,725 @@
|
|
1
|
+
var HIGHLIGHT = '#cc00cc';
|
2
|
+
var REGULAR = '#666666';
|
3
|
+
|
4
|
+
var cols = {
|
5
|
+
'a':173,
|
6
|
+
'b':325,
|
7
|
+
'c':589,
|
8
|
+
'd':797,
|
9
|
+
'e':1005,
|
10
|
+
'f':1195,
|
11
|
+
'g':1402,
|
12
|
+
'gg':1515,
|
13
|
+
'h':1572,
|
14
|
+
'i':1799,
|
15
|
+
'j':1893,
|
16
|
+
'k':1988,
|
17
|
+
'l':2157,
|
18
|
+
'll':2346,
|
19
|
+
'm':2403,
|
20
|
+
'mm':2535,
|
21
|
+
'n':2554,
|
22
|
+
'o':2649,
|
23
|
+
'oo':2781,
|
24
|
+
'ooo':2801,
|
25
|
+
'p':2894,
|
26
|
+
'q':3007
|
27
|
+
};
|
28
|
+
|
29
|
+
var rows = {
|
30
|
+
'1':221,
|
31
|
+
'2':298,
|
32
|
+
'3':373,
|
33
|
+
'4':448,
|
34
|
+
'5':524,
|
35
|
+
'6':599,
|
36
|
+
'7':675,
|
37
|
+
'8':751,
|
38
|
+
'9':826,
|
39
|
+
'10':902,
|
40
|
+
'11':977,
|
41
|
+
'12':1053,
|
42
|
+
'13':1129,
|
43
|
+
'14':1204,
|
44
|
+
'15':1280,
|
45
|
+
'16':1355,
|
46
|
+
'17':1431,
|
47
|
+
'18':1506,
|
48
|
+
'19':1583,
|
49
|
+
'20':1658,
|
50
|
+
'21':1734,
|
51
|
+
'22':1809,
|
52
|
+
'23':1885,
|
53
|
+
'24':1961,
|
54
|
+
'25':2036,
|
55
|
+
'26':2112
|
56
|
+
};
|
57
|
+
|
58
|
+
var edges = {
|
59
|
+
'b14b13':['b14','b13'],
|
60
|
+
|
61
|
+
'b13b12':['b13','b12'],
|
62
|
+
'b13503':['b13','503'],
|
63
|
+
|
64
|
+
'b12b11':['b12','b11'],
|
65
|
+
'b12501':['b12','501'],
|
66
|
+
|
67
|
+
'b11b10':['b11','b10'],
|
68
|
+
'b11414':['b11','414'],
|
69
|
+
|
70
|
+
'b10b9':['b10','b9'],
|
71
|
+
'b10405':['b10','405'],
|
72
|
+
|
73
|
+
'b9b8':['b9','b8'],
|
74
|
+
'b9400':['b9','400'],
|
75
|
+
|
76
|
+
'b8b7':['b8','b7'],
|
77
|
+
'b8401':['b8','401'],
|
78
|
+
|
79
|
+
'b7b6':['b7','b6'],
|
80
|
+
'b7403':['b7','403'],
|
81
|
+
|
82
|
+
'b6b5':['b6','b5'],
|
83
|
+
'b6501':['b6','501a'],
|
84
|
+
|
85
|
+
'b5b4':['b5','b4'],
|
86
|
+
'b5415':['b5','415'],
|
87
|
+
|
88
|
+
'b4b3':['b4','b3'],
|
89
|
+
'b4413':['b4','b4'],
|
90
|
+
|
91
|
+
'b3c3':['b3','c3'],
|
92
|
+
'b3200':['b3','200'],
|
93
|
+
|
94
|
+
'c3c4':['c3','c4'],
|
95
|
+
'c3d4':['c3','d3','d4'],
|
96
|
+
|
97
|
+
'c4d4':['c4','d4'],
|
98
|
+
'c4406':['c4','406'],
|
99
|
+
|
100
|
+
'd4d5':['d4','d5'],
|
101
|
+
'd4e5':['d4','e4','e5'],
|
102
|
+
|
103
|
+
'd5e5':['d5','e5'],
|
104
|
+
'd5406':['d5','d7','406'],
|
105
|
+
|
106
|
+
'e5e6':['e5','e6'],
|
107
|
+
'e5f6':['e5','f5','f6'],
|
108
|
+
|
109
|
+
'e6f6':['e6','f6'],
|
110
|
+
'e6406':['e6','e7','406'],
|
111
|
+
|
112
|
+
'f6f7':['f6','f7'],
|
113
|
+
'f6g7':['f6','g6','g7'],
|
114
|
+
|
115
|
+
'f7g7':['f7','g7'],
|
116
|
+
'f7406':['f7','406'],
|
117
|
+
|
118
|
+
'g7g8':['g7','g8'],
|
119
|
+
'g7h7':['g7','h7'],
|
120
|
+
|
121
|
+
'g8g9':['g8','g9'],
|
122
|
+
'g8h10':['g8','h8','h10'],
|
123
|
+
|
124
|
+
'g9g11':['g9','g11'],
|
125
|
+
'g9h10':['g9','gg9','gg10','h10'],
|
126
|
+
|
127
|
+
'g11h10':['g11','gg11','gg10','h10'],
|
128
|
+
'g11412':['g11','g18','412a'],
|
129
|
+
|
130
|
+
'h7i7':['h7','i7'],
|
131
|
+
'h7412':['h7','412'],
|
132
|
+
|
133
|
+
'h10h11':['h10','h11'],
|
134
|
+
'h10i12':['h10','i10','i12'],
|
135
|
+
|
136
|
+
'h11h12':['h11','h12'],
|
137
|
+
'h11i12':['h11','i11','i12'],
|
138
|
+
|
139
|
+
'h12i12':['h12','i12'],
|
140
|
+
'h12412':['h12','412a'],
|
141
|
+
|
142
|
+
'i4p3':['i4','i3','p3'],
|
143
|
+
'i4301':['i4','301'],
|
144
|
+
|
145
|
+
'i7i4':['i7','i4'],
|
146
|
+
'i7k7':['i7','k7'],
|
147
|
+
|
148
|
+
'i12l13':['i12','l12','l13'],
|
149
|
+
'i12i13':['i12','i13'],
|
150
|
+
|
151
|
+
'i13k13':['i13','k13'],
|
152
|
+
'i13j18':['i13','i17','j17','j18'],
|
153
|
+
|
154
|
+
'j18412':['j18','412a'],
|
155
|
+
'j18304':['j18','304'],
|
156
|
+
|
157
|
+
'k5l5':['k5','l5'],
|
158
|
+
'k5301':['k5','301'],
|
159
|
+
|
160
|
+
'k7k5':['k7','k5'],
|
161
|
+
'k7l7':['k7','l7'],
|
162
|
+
|
163
|
+
'k13j18':['k13','k17','j17','j18'],
|
164
|
+
'k13l13':['k13','l13'],
|
165
|
+
|
166
|
+
'l5m5':['l5','m5'],
|
167
|
+
'l5307':['l5','307'],
|
168
|
+
|
169
|
+
'l7m7':['l7','m7'],
|
170
|
+
'l7404':['l7','l8','404'],
|
171
|
+
|
172
|
+
'l13l14':['l13','l14'],
|
173
|
+
'l13m16':['l13','m13','m16'],
|
174
|
+
|
175
|
+
'l14l15':['l14','l15'],
|
176
|
+
'l14m16':['l14','m14','m16'],
|
177
|
+
|
178
|
+
'l15l17':['l15','l17'],
|
179
|
+
'l15m16':['l15','ll15','ll16','m16'],
|
180
|
+
|
181
|
+
'l17m16':['l17','ll17','ll16','m16'],
|
182
|
+
'l17304':['l17','304'],
|
183
|
+
|
184
|
+
'm5n5':['m5','n5'],
|
185
|
+
'm5410':['m5','m4','410'],
|
186
|
+
|
187
|
+
'm7n11':['m7','n7','n11'],
|
188
|
+
'm7404':['m7','404'],
|
189
|
+
|
190
|
+
'm16m20':['m16','m20'],
|
191
|
+
'm16n16':['m16','n16'],
|
192
|
+
|
193
|
+
'm20o20':['m20','o20'],
|
194
|
+
'm20202':['m20','202'],
|
195
|
+
|
196
|
+
'n5n11':['n5','n11'],
|
197
|
+
'n5410':['n5','410'],
|
198
|
+
|
199
|
+
'n11p11':['n11','p11'],
|
200
|
+
'n11303':['n11','303'],
|
201
|
+
|
202
|
+
'n16n11':['n16','n11'],
|
203
|
+
'n16o16':['n16','o16'],
|
204
|
+
|
205
|
+
'o14p11':['o14','o11','p11'],
|
206
|
+
'o14409':['o14','409a'],
|
207
|
+
|
208
|
+
'o16o14':['o16','o14'],
|
209
|
+
'o16o18':['o16','o18'],
|
210
|
+
|
211
|
+
'o18200':['o18','200a'],
|
212
|
+
'o18300':['o18','oo18','300'],
|
213
|
+
|
214
|
+
'o20o18':['o20','o18'],
|
215
|
+
'o20204':['o20','204'],
|
216
|
+
|
217
|
+
'p3p11':['p3','p11'],
|
218
|
+
'p3409':['p3','409'],
|
219
|
+
|
220
|
+
'p11o20':['p11','p20','o20'],
|
221
|
+
'p11201':['p11','q11','201']
|
222
|
+
};
|
223
|
+
|
224
|
+
var ends = {
|
225
|
+
'200': {col:'a', row:'3', width:190},
|
226
|
+
'200a': {col:'mm', row:'18', width:116},
|
227
|
+
'201': {col:'q', row:'12', width:154},
|
228
|
+
'202': {col:'m', row:'21', width:116},
|
229
|
+
'204': {col:'o', row:'21', width:152},
|
230
|
+
|
231
|
+
'300': {col:'oo', row:'19', width:152},
|
232
|
+
'301': {col:'k', row:'4', width:154},
|
233
|
+
'303': {col:'m', row:'11', width:116},
|
234
|
+
'304': {col:'l', row:'18', width:116},
|
235
|
+
'307': {col:'l', row:'4', width:154},
|
236
|
+
|
237
|
+
'400': {col:'a', row:'9', width:190},
|
238
|
+
'401': {col:'a', row:'8', width:190},
|
239
|
+
'403': {col:'a', row:'7', width:190},
|
240
|
+
'404': {col:'m', row:'8', width:116},
|
241
|
+
'405': {col:'a', row:'10', width:190},
|
242
|
+
'406': {col:'c', row:'7', width:152},
|
243
|
+
'409': {col:'p', row:'2', width:116},
|
244
|
+
'409a': {col:'oo', row:'14', width:116},
|
245
|
+
'410': {col:'n', row:'4', width:116},
|
246
|
+
'412': {col:'h', row:'6', width:152},
|
247
|
+
'412a': {col:'h', row:'18', width:152},
|
248
|
+
'413': {col:'a', row:'4', width:190},
|
249
|
+
'414': {col:'a', row:'11', width:190},
|
250
|
+
'415': {col:'a', row:'5', width:190},
|
251
|
+
|
252
|
+
'501a': {col:'a', row:'6', width:190},
|
253
|
+
'501': {col:'a', row:'12', width:190},
|
254
|
+
'503': {col:'a', row:'13', width:190}
|
255
|
+
};
|
256
|
+
|
257
|
+
var canvas;
|
258
|
+
|
259
|
+
function decorateTrace() {
|
260
|
+
trace[0].x = cols[trace[0].d[0]];
|
261
|
+
trace[0].y = rows[trace[0].d.slice(1)];
|
262
|
+
trace[0].previewCalls = previewCalls(trace[0]);
|
263
|
+
|
264
|
+
for (var i = 1; i < trace.length; i++) {
|
265
|
+
trace[i].x = cols[trace[i].d[0]];
|
266
|
+
trace[i].y = rows[trace[i].d.slice(1)];
|
267
|
+
trace[i].previewCalls = previewCalls(trace[i]);
|
268
|
+
|
269
|
+
var path = edges[trace[i-1].d+trace[i].d];
|
270
|
+
if (path) {
|
271
|
+
trace[i].path = [path.length-1];
|
272
|
+
for (var p = 1; p < path.length; p++) {
|
273
|
+
trace[i].path[p-1] = getSeg(path[p-1], path[p], p == path.length-1);
|
274
|
+
}
|
275
|
+
} else {
|
276
|
+
trace[i].path = [];
|
277
|
+
}
|
278
|
+
}
|
279
|
+
|
280
|
+
var path = edges[trace[i-1].d+response.code];
|
281
|
+
if (path) {
|
282
|
+
var end = ends[path[path.length-1]];
|
283
|
+
response.x = cols[end.col];
|
284
|
+
response.y = rows[end.row];
|
285
|
+
response.width = end.width;
|
286
|
+
response.type = 'normal';
|
287
|
+
|
288
|
+
response.path = [path.length-1];
|
289
|
+
for (var p = 1; p < path.length; p++) {
|
290
|
+
response.path[p-1] = getSeg(path[p-1], path[p], p == path.length-1);
|
291
|
+
}
|
292
|
+
} else {
|
293
|
+
var ld = trace[trace.length-1];
|
294
|
+
response.x = ld.x+50;
|
295
|
+
response.y = ld.y-50;
|
296
|
+
response.width = 38;
|
297
|
+
response.type = 'other';
|
298
|
+
|
299
|
+
response.path = [
|
300
|
+
{x1: ld.x+10, y1: ld.y-10,
|
301
|
+
x2: ld.x+36, y2: ld.y-36}
|
302
|
+
];
|
303
|
+
}
|
304
|
+
};
|
305
|
+
|
306
|
+
function previewCalls(dec) {
|
307
|
+
var prev = '';
|
308
|
+
for (var i = 0; i < dec.calls.length; i++) {
|
309
|
+
if(dec.calls[i].call.indexOf("(default)") !== 0) {
|
310
|
+
prev += '<li>'+dec.calls[i].call;
|
311
|
+
if(dec.calls[i].source !== null)
|
312
|
+
prev += " (" + dec.calls[i].source + ")";
|
313
|
+
prev+='</li>';
|
314
|
+
}
|
315
|
+
}
|
316
|
+
return prev;
|
317
|
+
};
|
318
|
+
|
319
|
+
function drawTrace() {
|
320
|
+
drawDecision(trace[0]);
|
321
|
+
for (var i = 1; i < trace.length; i++) {
|
322
|
+
drawPath(trace[i].path);
|
323
|
+
drawDecision(trace[i]);
|
324
|
+
}
|
325
|
+
|
326
|
+
drawPath(response.path);
|
327
|
+
drawResponse();
|
328
|
+
};
|
329
|
+
|
330
|
+
function drawResponse() {
|
331
|
+
if (response.type == 'normal') {
|
332
|
+
var context = canvas.getContext('2d');
|
333
|
+
context.strokeStyle=HIGHLIGHT;
|
334
|
+
context.lineWidth=4;
|
335
|
+
|
336
|
+
context.beginPath();
|
337
|
+
context.rect(response.x-(response.width/2),
|
338
|
+
response.y-19,
|
339
|
+
response.width,
|
340
|
+
38);
|
341
|
+
context.stroke();
|
342
|
+
} else {
|
343
|
+
var context = canvas.getContext('2d');
|
344
|
+
context.strokeStyle='#ff0000';
|
345
|
+
context.lineWidth=4;
|
346
|
+
|
347
|
+
context.beginPath();
|
348
|
+
context.arc(response.x, response.y, 19,
|
349
|
+
0, 2*3.14159, false);
|
350
|
+
context.stroke();
|
351
|
+
|
352
|
+
}
|
353
|
+
};
|
354
|
+
|
355
|
+
function drawDecision(dec) {
|
356
|
+
var context = canvas.getContext('2d');
|
357
|
+
|
358
|
+
if (dec.previewCalls == '')
|
359
|
+
context.strokeStyle=REGULAR;
|
360
|
+
else
|
361
|
+
context.strokeStyle=HIGHLIGHT;
|
362
|
+
context.lineWidth=4;
|
363
|
+
|
364
|
+
context.beginPath();
|
365
|
+
context.moveTo(dec.x, dec.y-19);
|
366
|
+
context.lineTo(dec.x+19, dec.y);
|
367
|
+
context.lineTo(dec.x, dec.y+19);
|
368
|
+
context.lineTo(dec.x-19, dec.y);
|
369
|
+
context.closePath();
|
370
|
+
context.stroke();
|
371
|
+
};
|
372
|
+
|
373
|
+
function drawPath(path) {
|
374
|
+
var context = canvas.getContext('2d');
|
375
|
+
context.strokeStyle=REGULAR;
|
376
|
+
context.lineWidth=4;
|
377
|
+
|
378
|
+
context.beginPath();
|
379
|
+
context.moveTo(path[0].x1, path[0].y1);
|
380
|
+
for (var p = 0; p < path.length; p++) {
|
381
|
+
context.lineTo(path[p].x2, path[p].y2);
|
382
|
+
}
|
383
|
+
context.stroke();
|
384
|
+
};
|
385
|
+
|
386
|
+
function getSeg(p1, p2, last) {
|
387
|
+
var seg = {
|
388
|
+
x1:cols[p1[0]],
|
389
|
+
y1:rows[p1.slice(1)]
|
390
|
+
};
|
391
|
+
if (ends[p2]) {
|
392
|
+
seg.x2 = cols[ends[p2].col];
|
393
|
+
seg.y2 = rows[ends[p2].row];
|
394
|
+
} else {
|
395
|
+
seg.x2 = cols[p2[0]];
|
396
|
+
seg.y2 = rows[p2.slice(1)];
|
397
|
+
}
|
398
|
+
|
399
|
+
if (seg.x1 == seg.x2) {
|
400
|
+
if (seg.y1 < seg.y2) {
|
401
|
+
seg.y1 = seg.y1+19;
|
402
|
+
if (last) seg.y2 = seg.y2-19;
|
403
|
+
} else {
|
404
|
+
seg.y1 = seg.y1-19;
|
405
|
+
if (last) seg.y2 = seg.y2+19;
|
406
|
+
}
|
407
|
+
} else {
|
408
|
+
//assume seg.y1 == seg.y2
|
409
|
+
if (seg.x1 < seg.x2) {
|
410
|
+
seg.x1 = seg.x1+19;
|
411
|
+
if (last) seg.x2 = seg.x2-(ends[p2] ? (ends[p2].width/2) : 19);
|
412
|
+
} else {
|
413
|
+
seg.x1 = seg.x1-19;
|
414
|
+
if (last) seg.x2 = seg.x2+(ends[p2] ? (ends[p2].width/2) : 19);
|
415
|
+
}
|
416
|
+
}
|
417
|
+
return seg;
|
418
|
+
};
|
419
|
+
|
420
|
+
function traceDecision(name) {
|
421
|
+
for (var i = trace.length-1; i >= 0; i--)
|
422
|
+
if (trace[i].d == name) return trace[i];
|
423
|
+
};
|
424
|
+
|
425
|
+
var detailPanels = {};
|
426
|
+
function initDetailPanels() {
|
427
|
+
var windowWidth = document.getElementById('sizetest').clientWidth;
|
428
|
+
var infoPanel = document.getElementById('infopanel');
|
429
|
+
var panelWidth = windowWidth-infoPanel.offsetLeft;
|
430
|
+
|
431
|
+
var panels = {
|
432
|
+
'request': document.getElementById('requestdetail'),
|
433
|
+
'response': document.getElementById('responsedetail'),
|
434
|
+
'decision': document.getElementById('decisiondetail')
|
435
|
+
};
|
436
|
+
|
437
|
+
var tabs = {
|
438
|
+
'request': document.getElementById('requesttab'),
|
439
|
+
'response': document.getElementById('responsetab'),
|
440
|
+
'decision': document.getElementById('decisiontab')
|
441
|
+
};
|
442
|
+
|
443
|
+
var decisionId = document.getElementById('decisionid');
|
444
|
+
var decisionCalls = document.getElementById('decisioncalls');
|
445
|
+
var callInput = document.getElementById('callinput');
|
446
|
+
var callOutput = document.getElementById('calloutput');
|
447
|
+
|
448
|
+
var lastUsedPanelWidth = windowWidth-infoPanel.offsetLeft;
|
449
|
+
|
450
|
+
var setPanelWidth = function(width) {
|
451
|
+
infoPanel.style.left = (windowWidth-width)+'px';
|
452
|
+
canvas.style.marginRight = (width+20)+'px';
|
453
|
+
panelWidth = width;
|
454
|
+
};
|
455
|
+
setPanelWidth(panelWidth);
|
456
|
+
|
457
|
+
var ensureVisible = function() {
|
458
|
+
if (windowWidth-infoPanel.offsetLeft < 10)
|
459
|
+
setPanelWidth(lastUsedPanelWidth);
|
460
|
+
};
|
461
|
+
|
462
|
+
var decChoices = '';
|
463
|
+
for (var i = 0; i < trace.length; i++) {
|
464
|
+
decChoices += '<option value="'+trace[i].d+'">'+trace[i].d+'</option>';
|
465
|
+
}
|
466
|
+
decisionId.innerHTML = decChoices;
|
467
|
+
decisionId.selectedIndex = -1;
|
468
|
+
|
469
|
+
decisionId.onchange = function() {
|
470
|
+
detailPanels.setDecision(traceDecision(decisionId.value));
|
471
|
+
}
|
472
|
+
|
473
|
+
detailPanels.setDecision = function(dec) {
|
474
|
+
decisionId.value = dec.d;
|
475
|
+
|
476
|
+
var calls = [];
|
477
|
+
for (var i = 0; i < dec.calls.length; i++) {
|
478
|
+
calls.push('<option value="'+dec.d+'-'+i+'">');
|
479
|
+
calls.push(dec.calls[i].call);
|
480
|
+
if(dec.calls[i].source !== null)
|
481
|
+
calls.push(' (' + dec.calls[i].source + ')');
|
482
|
+
calls.push('</option>');
|
483
|
+
}
|
484
|
+
decisionCalls.innerHTML = calls.join('');
|
485
|
+
decisionCalls.selectedIndex = 0;
|
486
|
+
|
487
|
+
decisionCalls.onchange();
|
488
|
+
};
|
489
|
+
|
490
|
+
detailPanels.show = function(name) {
|
491
|
+
for (p in panels) {
|
492
|
+
if (p == name) {
|
493
|
+
panels[p].style.display = 'block';
|
494
|
+
tabs[p].className = 'selectedtab';
|
495
|
+
}
|
496
|
+
else {
|
497
|
+
panels[p].style.display = 'none';
|
498
|
+
tabs[p].className = '';
|
499
|
+
}
|
500
|
+
}
|
501
|
+
ensureVisible();
|
502
|
+
};
|
503
|
+
|
504
|
+
detailPanels.hide = function() {
|
505
|
+
setPanelWidth(0);
|
506
|
+
}
|
507
|
+
|
508
|
+
decisionCalls.onchange = function() {
|
509
|
+
var val = decisionCalls.value;
|
510
|
+
if (val) {
|
511
|
+
var dec = traceDecision(val.substring(0, val.indexOf('-')));
|
512
|
+
var call = dec.calls[parseInt(val.substring(val.indexOf('-')+1, val.length))];
|
513
|
+
|
514
|
+
if (call.call.indexOf("(default)") !== 0) {
|
515
|
+
callInput.style.color='#000000';
|
516
|
+
callInput.textContent = call.input;
|
517
|
+
if (call.output != null) {
|
518
|
+
callOutput.style.color = '#000000';
|
519
|
+
callOutput.textContent = call.output;
|
520
|
+
} else {
|
521
|
+
if(call.exception !== null){
|
522
|
+
callOutput.style.color = '#ff0000';
|
523
|
+
callOutput.textContent = 'Exception raised!\n\n' + call.exception['class'] + ': ' +
|
524
|
+
call.exception.message + '\n ' + call.exception.backtrace.split('\n').join('\n ');
|
525
|
+
}
|
526
|
+
}
|
527
|
+
} else {
|
528
|
+
callInput.style.color='#999999';
|
529
|
+
callInput.textContent = call.call.replace('(default)', '') + " was not overridden by the resource";
|
530
|
+
callOutput.textContent = '';
|
531
|
+
}
|
532
|
+
} else {
|
533
|
+
callInput.textContent = '';
|
534
|
+
callOutput.textContent = '';
|
535
|
+
}
|
536
|
+
};
|
537
|
+
|
538
|
+
var headersList = function(headers) {
|
539
|
+
var h = '';
|
540
|
+
for (n in headers) h += '<tr><th><code>'+n+':</code></th><td><code>'+headers[n] + '</code></td></tr>';
|
541
|
+
return h;
|
542
|
+
};
|
543
|
+
|
544
|
+
document.getElementById('requestmethod').innerHTML = request.method;
|
545
|
+
document.getElementById('requestpath').innerHTML = request.path;
|
546
|
+
document.getElementById('requestheaders').innerHTML = headersList(request.headers);
|
547
|
+
document.getElementById('requestbody').textContent = request.body;
|
548
|
+
|
549
|
+
document.getElementById('responsecode').innerHTML = response.code;
|
550
|
+
document.getElementById('responseheaders').innerHTML = headersList(response.headers);
|
551
|
+
document.getElementById('responsebody').textContent = response.body;
|
552
|
+
|
553
|
+
|
554
|
+
var infoControls = document.getElementById('infocontrols');
|
555
|
+
var md = false;
|
556
|
+
var dragged = false;
|
557
|
+
var msoff = 0;
|
558
|
+
infoControls.onmousedown = function(ev) {
|
559
|
+
md = true;
|
560
|
+
dragged = false;
|
561
|
+
msoff = ev.clientX-infoPanel.offsetLeft;
|
562
|
+
};
|
563
|
+
|
564
|
+
infoControls.onclick = function(ev) {
|
565
|
+
if (dragged) {
|
566
|
+
lastUsedPanelWidth = panelWidth;
|
567
|
+
}
|
568
|
+
else if (panelWidth < 10) {
|
569
|
+
switch(ev.target.id) {
|
570
|
+
case 'requesttab': detailPanels.show('request'); break;
|
571
|
+
case 'responsetab': detailPanels.show('response'); break;
|
572
|
+
case 'decisiontab': detailPanels.show('decision'); break;
|
573
|
+
default: ensureVisible();
|
574
|
+
}
|
575
|
+
} else {
|
576
|
+
var name = 'none';
|
577
|
+
switch(ev.target.id) {
|
578
|
+
case 'requesttab': name = 'request'; break;
|
579
|
+
case 'responsetab': name = 'response'; break;
|
580
|
+
case 'decisiontab': name = 'decision'; break;
|
581
|
+
}
|
582
|
+
|
583
|
+
if (panels[name] && panels[name].style.display != 'block')
|
584
|
+
detailPanels.show(name);
|
585
|
+
else
|
586
|
+
detailPanels.hide();
|
587
|
+
}
|
588
|
+
|
589
|
+
return false;
|
590
|
+
};
|
591
|
+
|
592
|
+
document.onmousemove = function(ev) {
|
593
|
+
if (md) {
|
594
|
+
dragged = true;
|
595
|
+
panelWidth = windowWidth-(ev.clientX-msoff);
|
596
|
+
if (panelWidth < 0) {
|
597
|
+
panelWidth = 0;
|
598
|
+
infoPanel.style.left = windowWidth+"px";
|
599
|
+
}
|
600
|
+
else if (panelWidth > windowWidth-21) {
|
601
|
+
panelWidth = windowWidth-21;
|
602
|
+
infoPanel.style.left = '21px';
|
603
|
+
}
|
604
|
+
else
|
605
|
+
infoPanel.style.left = (ev.clientX-msoff)+"px";
|
606
|
+
|
607
|
+
canvas.style.marginRight = panelWidth+20+"px";
|
608
|
+
return false;
|
609
|
+
}
|
610
|
+
};
|
611
|
+
|
612
|
+
document.onmouseup = function() { md = false; };
|
613
|
+
|
614
|
+
window.onresize = function() {
|
615
|
+
windowWidth = document.getElementById('sizetest').clientWidth;
|
616
|
+
infoPanel.style.left = windowWidth-panelWidth+'px';
|
617
|
+
};
|
618
|
+
|
619
|
+
detailPanels.setDecision(trace[0]);
|
620
|
+
};
|
621
|
+
|
622
|
+
window.onload = function() {
|
623
|
+
canvas = document.getElementById('v3map');
|
624
|
+
|
625
|
+
initDetailPanels();
|
626
|
+
|
627
|
+
var scale = 0.25;
|
628
|
+
var coy = canvas.offsetTop;
|
629
|
+
function findDecision(ev) {
|
630
|
+
var x = (ev.clientX+window.pageXOffset)/scale;
|
631
|
+
var y = (ev.clientY+window.pageYOffset-coy)/scale;
|
632
|
+
|
633
|
+
for (var i = trace.length-1; i >= 0; i--) {
|
634
|
+
if (x >= trace[i].x-19 && x <= trace[i].x+19 &&
|
635
|
+
y >= trace[i].y-19 && y <= trace[i].y+19)
|
636
|
+
return trace[i];
|
637
|
+
}
|
638
|
+
};
|
639
|
+
|
640
|
+
var preview = document.getElementById('preview');
|
641
|
+
var previewId = document.getElementById('previewid');
|
642
|
+
var previewCalls = document.getElementById('previewcalls');
|
643
|
+
function previewDecision(dec) {
|
644
|
+
preview.style.left = (dec.x*scale)+'px';
|
645
|
+
preview.style.top = (dec.y*scale+coy+15)+'px';
|
646
|
+
preview.style.display = 'block';
|
647
|
+
previewId.textContent = dec.d;
|
648
|
+
|
649
|
+
previewCalls.innerHTML = dec.previewCalls;
|
650
|
+
};
|
651
|
+
|
652
|
+
function overResponse(ev) {
|
653
|
+
var x = (ev.clientX+window.pageXOffset)/scale;
|
654
|
+
var y = (ev.clientY+window.pageYOffset-coy)/scale;
|
655
|
+
|
656
|
+
return (x >= response.x-(response.width/2)
|
657
|
+
&& x <= response.x+(response.width/2)
|
658
|
+
&& y >= response.y-19 && y <= response.y+19);
|
659
|
+
};
|
660
|
+
|
661
|
+
decorateTrace();
|
662
|
+
|
663
|
+
var bg = new Image(3138, 2184);
|
664
|
+
|
665
|
+
function drawMap() {
|
666
|
+
var ctx = canvas.getContext("2d");
|
667
|
+
|
668
|
+
ctx.save();
|
669
|
+
ctx.scale(1/scale, 1/scale);
|
670
|
+
ctx.fillStyle = '#ffffff';
|
671
|
+
ctx.fillRect(0, 0, 3138, 2184);
|
672
|
+
ctx.restore();
|
673
|
+
|
674
|
+
ctx.drawImage(bg, 0, 0);
|
675
|
+
drawTrace();
|
676
|
+
};
|
677
|
+
|
678
|
+
bg.onload = function() {
|
679
|
+
canvas.getContext("2d").scale(scale, scale);
|
680
|
+
|
681
|
+
document.getElementById('zoomin').onclick = function() {
|
682
|
+
scale = scale*2;
|
683
|
+
canvas.getContext("2d").scale(2, 2);
|
684
|
+
drawMap();
|
685
|
+
};
|
686
|
+
|
687
|
+
document.getElementById('zoomout').onclick = function() {
|
688
|
+
scale = scale/2;
|
689
|
+
canvas.getContext("2d").scale(0.5, 0.5);
|
690
|
+
drawMap();
|
691
|
+
};
|
692
|
+
|
693
|
+
drawMap(scale);
|
694
|
+
|
695
|
+
canvas.onmousemove = function(ev) {
|
696
|
+
if (findDecision(ev)) {
|
697
|
+
canvas.style.cursor = 'pointer';
|
698
|
+
previewDecision(findDecision(ev));
|
699
|
+
}
|
700
|
+
else {
|
701
|
+
preview.style.display = 'none';
|
702
|
+
if (overResponse(ev))
|
703
|
+
canvas.style.cursor = 'pointer';
|
704
|
+
else
|
705
|
+
canvas.style.cursor = 'default';
|
706
|
+
}
|
707
|
+
};
|
708
|
+
|
709
|
+
canvas.onclick = function(ev) {
|
710
|
+
var dec = findDecision(ev);
|
711
|
+
if (dec) {
|
712
|
+
detailPanels.setDecision(dec);
|
713
|
+
detailPanels.show('decision');
|
714
|
+
} else if (overResponse(ev)) {
|
715
|
+
detailPanels.show('response');
|
716
|
+
}
|
717
|
+
};
|
718
|
+
};
|
719
|
+
|
720
|
+
bg.onerror = function() {
|
721
|
+
alert('Failed to load background image.');
|
722
|
+
};
|
723
|
+
|
724
|
+
bg.src = 'static/map.png';
|
725
|
+
};
|