flammarion 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +19 -0
- data/Readme.md +74 -0
- data/lib/flammarion.rb +155 -0
- data/lib/flammarion/pane.rb +12 -0
- data/lib/flammarion/server.rb +103 -0
- data/lib/flammarion/version.rb +3 -0
- data/lib/flammarion/writeable.rb +202 -0
- data/lib/html/Gemfile +19 -0
- data/lib/html/Gemfile.lock +151 -0
- data/lib/html/build/images/layers-2x.png +0 -0
- data/lib/html/build/images/layers.png +0 -0
- data/lib/html/build/images/marker-icon-2x.png +0 -0
- data/lib/html/build/images/marker-icon.png +0 -0
- data/lib/html/build/images/marker-shadow.png +0 -0
- data/lib/html/build/index.html +1 -0
- data/lib/html/build/javascripts/actions.js +415 -0
- data/lib/html/build/javascripts/all.js +7401 -0
- data/lib/html/build/javascripts/map.js +222 -0
- data/lib/html/build/javascripts/plot.js +288 -0
- data/lib/html/build/javascripts/querystring.js +39 -0
- data/lib/html/build/javascripts/status.js +37 -0
- data/lib/html/build/javascripts/vendor/ansi_up.js +327 -0
- data/lib/html/build/javascripts/vendor/highlight.pack.js +1 -0
- data/lib/html/build/javascripts/vendor/jquery.js +5 -0
- data/lib/html/build/javascripts/vendor/jquery.transit.min.js +1 -0
- data/lib/html/build/javascripts/vendor/l.control.geosearch.js +253 -0
- data/lib/html/build/javascripts/vendor/l.geosearch.provider.openstreetmap.js +297 -0
- data/lib/html/build/javascripts/vendor/leaflet.js +10 -0
- data/lib/html/build/javascripts/vendor/term.js +5974 -0
- data/lib/html/build/javascripts/websocket.js +166 -0
- data/lib/html/build/spectrum_test.html +12 -0
- data/lib/html/build/stylesheets/all.css +992 -0
- data/lib/html/build/stylesheets/buttons.css +179 -0
- data/lib/html/build/stylesheets/colors.css +0 -0
- data/lib/html/build/stylesheets/frontend.css +112 -0
- data/lib/html/build/stylesheets/leaflet.css +479 -0
- data/lib/html/build/stylesheets/map.css +0 -0
- data/lib/html/build/stylesheets/normalize.bak +375 -0
- data/lib/html/build/stylesheets/railscasts.css +185 -0
- data/lib/html/build/stylesheets/scrollbar.css +14 -0
- data/lib/html/build/stylesheets/table.css +21 -0
- data/lib/html/build/term_test.html +28 -0
- data/lib/html/config.rb +72 -0
- data/lib/html/source/images/layers-2x.png +0 -0
- data/lib/html/source/images/layers.png +0 -0
- data/lib/html/source/images/marker-icon-2x.png +0 -0
- data/lib/html/source/images/marker-icon.png +0 -0
- data/lib/html/source/images/marker-shadow.png +0 -0
- data/lib/html/source/index.html.slim +31 -0
- data/lib/html/source/javascripts/actions.coffee +192 -0
- data/lib/html/source/javascripts/all.js +3 -0
- data/lib/html/source/javascripts/map.coffee +43 -0
- data/lib/html/source/javascripts/plot.coffee +233 -0
- data/lib/html/source/javascripts/querystring.coffee +12 -0
- data/lib/html/source/javascripts/status.coffee +17 -0
- data/lib/html/source/javascripts/vendor/ansi_up.js +348 -0
- data/lib/html/source/javascripts/vendor/highlight.pack.js +1 -0
- data/lib/html/source/javascripts/vendor/jquery.js +4 -0
- data/lib/html/source/javascripts/vendor/jquery.transit.min.js +1 -0
- data/lib/html/source/javascripts/vendor/l.control.geosearch.js +242 -0
- data/lib/html/source/javascripts/vendor/l.geosearch.provider.openstreetmap.js +43 -0
- data/lib/html/source/javascripts/vendor/leaflet.js +9 -0
- data/lib/html/source/javascripts/vendor/term.js +5973 -0
- data/lib/html/source/javascripts/websocket.coffee +83 -0
- data/lib/html/source/layouts/layout.erb +0 -0
- data/lib/html/source/spectrum_test.html.slim +39 -0
- data/lib/html/source/stylesheets/all.css +2 -0
- data/lib/html/source/stylesheets/buttons.styl +115 -0
- data/lib/html/source/stylesheets/colors.styl +18 -0
- data/lib/html/source/stylesheets/frontend.styl +109 -0
- data/lib/html/source/stylesheets/leaflet.css +478 -0
- data/lib/html/source/stylesheets/map.styl +0 -0
- data/lib/html/source/stylesheets/normalize.bak +375 -0
- data/lib/html/source/stylesheets/railscasts.css +184 -0
- data/lib/html/source/stylesheets/scrollbar.styl +18 -0
- data/lib/html/source/stylesheets/table.styl +28 -0
- data/lib/html/source/term_test.html.slim +50 -0
- metadata +250 -0
@@ -0,0 +1,21 @@
|
|
1
|
+
table {
|
2
|
+
border-collapse: collapse;
|
3
|
+
}
|
4
|
+
td {
|
5
|
+
border: none;
|
6
|
+
padding: 0.4em;
|
7
|
+
box-sizing: padding-box;
|
8
|
+
}
|
9
|
+
td.hover {
|
10
|
+
background: #240030;
|
11
|
+
}
|
12
|
+
tr {
|
13
|
+
padding: 0;
|
14
|
+
}
|
15
|
+
tr:hover {
|
16
|
+
background: #240030;
|
17
|
+
}
|
18
|
+
tr:hover td.hover {
|
19
|
+
outline: 1px solid #333;
|
20
|
+
background: #120018;
|
21
|
+
}
|
@@ -0,0 +1,28 @@
|
|
1
|
+
<!DOCTYPE html><html><head><meta content="text/html; charset=UTF-8" http-equiv="content-type" /><meta charset="utf-8" /><meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible" /><meta content="width=device-width, initial-scale=1.0" name="viewport" /><link href="stylesheets/railscasts.css" rel="stylesheet" type="text/css" /><link href="stylesheets/all.css" rel="stylesheet" type="text/css" /><link href="stylesheets/scrollbar.css" rel="stylesheet" type="text/css" /><script src="javascripts/all.js" type="text/javascript"></script><script>hljs.initHighlightingOnLoad();</script><script>(function() {
|
2
|
+
$(document).ready(function() {
|
3
|
+
var term;
|
4
|
+
term = new Terminal({
|
5
|
+
cols: 80,
|
6
|
+
rows: 24,
|
7
|
+
screenKeys: true
|
8
|
+
});
|
9
|
+
term.on('data', function(data) {
|
10
|
+
return console.log(data);
|
11
|
+
});
|
12
|
+
term.on('title', function(title) {
|
13
|
+
return document.title = title;
|
14
|
+
});
|
15
|
+
term.open($('#console-default')[0]);
|
16
|
+
return term.write('HEllo World');
|
17
|
+
});
|
18
|
+
|
19
|
+
}).call(this);
|
20
|
+
</script><script>(function() {
|
21
|
+
$.extend(WSClient.prototype.actions, {
|
22
|
+
term: function(data) {
|
23
|
+
return term.write(data.data);
|
24
|
+
}
|
25
|
+
});
|
26
|
+
|
27
|
+
}).call(this);
|
28
|
+
</script></head><body><div class="hidden" id="toolbar"><a class="tool-button" href="#">Open</a><a class="tool-button" href="#">Save</a></div><div id="panes"><pre class="pane" id="console-default" style="height:100%"></pre></div><div class="hidden" id="dialog"><pre id="message">Hi World</pre><a class="full-button" href="#">Ok</a></div><div id="status"><div class="left"></div><div class="center"></div><div class="right"></div></div></body></html>
|
data/lib/html/config.rb
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
###
|
2
|
+
# Compass
|
3
|
+
###
|
4
|
+
|
5
|
+
# Change Compass configuration
|
6
|
+
# compass_config do |config|
|
7
|
+
# config.output_style = :compact
|
8
|
+
# end
|
9
|
+
|
10
|
+
###
|
11
|
+
# Page options, layouts, aliases and proxies
|
12
|
+
###
|
13
|
+
|
14
|
+
# Per-page layout changes:
|
15
|
+
#
|
16
|
+
# With no layout
|
17
|
+
# page "/path/to/file.html", :layout => false
|
18
|
+
#
|
19
|
+
# With alternative layout
|
20
|
+
# page "/path/to/file.html", :layout => :otherlayout
|
21
|
+
#
|
22
|
+
# A path which all have the same layout
|
23
|
+
# with_layout :admin do
|
24
|
+
# page "/admin/*"
|
25
|
+
# end
|
26
|
+
|
27
|
+
# Proxy pages (https://middlemanapp.com/advanced/dynamic_pages/)
|
28
|
+
# proxy "/this-page-has-no-template.html", "/template-file.html", :locals => {
|
29
|
+
# :which_fake_page => "Rendering a fake page with a local variable" }
|
30
|
+
|
31
|
+
###
|
32
|
+
# Helpers
|
33
|
+
###
|
34
|
+
|
35
|
+
# Automatic image dimensions on image_tag helper
|
36
|
+
# activate :automatic_image_sizes
|
37
|
+
|
38
|
+
# Reload the browser automatically whenever files change
|
39
|
+
configure :development do
|
40
|
+
activate :livereload
|
41
|
+
end
|
42
|
+
|
43
|
+
# Methods defined in the helpers block are available in templates
|
44
|
+
# helpers do
|
45
|
+
# def some_helper
|
46
|
+
# "Helping"
|
47
|
+
# end
|
48
|
+
# end
|
49
|
+
|
50
|
+
set :css_dir, 'stylesheets'
|
51
|
+
|
52
|
+
set :js_dir, 'javascripts'
|
53
|
+
|
54
|
+
set :images_dir, 'images'
|
55
|
+
|
56
|
+
# Build-specific configuration
|
57
|
+
configure :build do
|
58
|
+
# For example, change the Compass output style for deployment
|
59
|
+
# activate :minify_css
|
60
|
+
|
61
|
+
# Minify Javascript on build
|
62
|
+
# activate :minify_javascript
|
63
|
+
|
64
|
+
# Enable cache buster
|
65
|
+
# activate :asset_hash
|
66
|
+
|
67
|
+
# Use relative URLs
|
68
|
+
activate :relative_assets
|
69
|
+
|
70
|
+
# Or use a different image path
|
71
|
+
# set :http_prefix, "/Content/images/"
|
72
|
+
end
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,31 @@
|
|
1
|
+
---
|
2
|
+
title: Welcome to Middleman
|
3
|
+
layout: false
|
4
|
+
---
|
5
|
+
|
6
|
+
doctype html
|
7
|
+
html
|
8
|
+
head
|
9
|
+
meta[http-equiv="content-type" content="text/html; charset=UTF-8"]
|
10
|
+
meta charset="utf-8"
|
11
|
+
meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible"
|
12
|
+
meta[name="viewport" content="width=device-width, initial-scale=1.0"]
|
13
|
+
title Flammarion
|
14
|
+
= stylesheet_link_tag("all")
|
15
|
+
= javascript_include_tag("all")
|
16
|
+
javascript:
|
17
|
+
hljs.initHighlightingOnLoad();
|
18
|
+
body
|
19
|
+
#toolbar.hidden
|
20
|
+
a.tool-button[href="#"] Open
|
21
|
+
a.tool-button[href="#"] Save
|
22
|
+
#panes
|
23
|
+
pre#console-default.pane[style="height:100%"]
|
24
|
+
#dialog.hidden
|
25
|
+
pre#message Hi World
|
26
|
+
a.full-button[href="#"] Ok
|
27
|
+
#status
|
28
|
+
.left
|
29
|
+
.center
|
30
|
+
.right
|
31
|
+
|
@@ -0,0 +1,192 @@
|
|
1
|
+
#= require websocket.coffee
|
2
|
+
$.extend WSClient.prototype.actions,
|
3
|
+
append: (data) ->
|
4
|
+
@__parent.check_target(data)
|
5
|
+
element = $("#console-#{data.target}")
|
6
|
+
marginSize = 16
|
7
|
+
atBottom = element.scrollTop() >= element[0].scrollHeight - element.height() - marginSize - 2 or element[0].scrollHeight - marginSize < element.height()
|
8
|
+
console.log "top: #{element.scrollTop()} height: #{element[0].scrollHeight - element.height() - marginSize} marginSize: #{marginSize} atBottom: #{atBottom}"
|
9
|
+
element.append(@__parent.escape(data.text, data))
|
10
|
+
element.scrollTop(element[0].scrollHeight - element.height() - marginSize) if atBottom
|
11
|
+
|
12
|
+
replace: (data) ->
|
13
|
+
@__parent.check_target(data)
|
14
|
+
$("#console-#{data.target}").html(@__parent.escape(data.text, data))
|
15
|
+
|
16
|
+
clear: (data) ->
|
17
|
+
@__parent.check_target(data)
|
18
|
+
$("#console-#{data.target}").html("") # empty() is really slow for some reason
|
19
|
+
|
20
|
+
addpane: (data) ->
|
21
|
+
if data.target
|
22
|
+
target = @__parent.check_target(data)
|
23
|
+
else
|
24
|
+
target = $('#panes')
|
25
|
+
|
26
|
+
target.append("<pre class='pane' id='console-#{data.name}'><pre>") if target.find("#console-#{data.name}").size() is 0
|
27
|
+
@__parent.resize_panes(data)
|
28
|
+
|
29
|
+
closepane: (data) ->
|
30
|
+
target = $("#console-#{data.target}")
|
31
|
+
unless target.size() is 0
|
32
|
+
target.remove()
|
33
|
+
@__parent.resize_panes(data)
|
34
|
+
|
35
|
+
hidepane: (data) ->
|
36
|
+
target = @__parent.check_target(data)
|
37
|
+
target.addClass("hidden")
|
38
|
+
target.removeClass("pane")
|
39
|
+
@__parent.resize_panes({target_element:target.parent()})
|
40
|
+
|
41
|
+
showpane: (data) ->
|
42
|
+
target = @__parent.check_target(data)
|
43
|
+
target.addClass("pane")
|
44
|
+
target.removeClass("hidden")
|
45
|
+
@__parent.resize_panes(data)
|
46
|
+
|
47
|
+
reorient: (data) ->
|
48
|
+
if data.target
|
49
|
+
target = @__parent.check_target(data)
|
50
|
+
else
|
51
|
+
target = $('#panes')
|
52
|
+
|
53
|
+
if data.orientation is "horizontal"
|
54
|
+
target.addClass("horizontal")
|
55
|
+
else
|
56
|
+
target.removeClass("horizontal")
|
57
|
+
|
58
|
+
@__parent.resize_panes(data)
|
59
|
+
|
60
|
+
# plot: (data) ->
|
61
|
+
# @__parent.check_target(data)
|
62
|
+
# drawPlot("#console-#{data.target}", data.data, $.extend({orientation:@__parent.orientation}, data))
|
63
|
+
|
64
|
+
highlight: (data) ->
|
65
|
+
target = @__parent.check_target(data)
|
66
|
+
code = $("<code>#{ansi_up.escape_for_html(data.text)}</code>")
|
67
|
+
code.addClass data.language if data.language
|
68
|
+
@__parent.add(code, target, data)
|
69
|
+
hljs.highlightBlock(code[0])
|
70
|
+
|
71
|
+
button: (data) ->
|
72
|
+
target = @__parent.check_target(data)
|
73
|
+
class_name = if data.inline then 'inline-button' else 'full-button'
|
74
|
+
element = $("<a href='#' class='#{class_name}'>#{@__parent.escape(data.label, data)}</a>")
|
75
|
+
element.click =>
|
76
|
+
@__parent.send({
|
77
|
+
id:data.id
|
78
|
+
action:'callback'
|
79
|
+
source:'button'
|
80
|
+
original_msg:data
|
81
|
+
})
|
82
|
+
target.append element
|
83
|
+
|
84
|
+
buttonbox: (data) ->
|
85
|
+
target = @__parent.check_target(data)
|
86
|
+
|
87
|
+
element = target.find("#console-#{data.name}")
|
88
|
+
if element.size() is 0
|
89
|
+
target.prepend("<pre class='button-box' id='console-#{data.name}'></pre>")
|
90
|
+
else
|
91
|
+
element.addClass('button-box')
|
92
|
+
|
93
|
+
break: (data) ->
|
94
|
+
target = @__parent.check_target(data)
|
95
|
+
code = $("<hr>")
|
96
|
+
@__parent.add(code, target, data)
|
97
|
+
|
98
|
+
subpane: (data) ->
|
99
|
+
target = @__parent.check_target(data)
|
100
|
+
element = target.find("#console-#{data.name}")
|
101
|
+
if element.size() is 0
|
102
|
+
target.append("<pre id='console-#{data.name}' class='pane'></pre>")
|
103
|
+
|
104
|
+
input: (data) ->
|
105
|
+
target = @__parent.check_target(data)
|
106
|
+
if data.multiline
|
107
|
+
element = $("<textarea placeholder='#{data.label}' class='inline-text-input'></textarea>")
|
108
|
+
else
|
109
|
+
element = $("<input type='text' placeholder='#{data.label}' class='inline-text-input'>")
|
110
|
+
if data.value
|
111
|
+
element[0].value = data.value
|
112
|
+
|
113
|
+
element.change =>
|
114
|
+
unless element.hasClass("unclicked")
|
115
|
+
@__parent.send({
|
116
|
+
id:data.id
|
117
|
+
action:'callback'
|
118
|
+
source:'input'
|
119
|
+
text: element[0].value
|
120
|
+
original_msg:data
|
121
|
+
})
|
122
|
+
if data.once
|
123
|
+
replaceText = @__parent.escape("#{element[0].value}\n")
|
124
|
+
replaceText = "#{data.label}#{replaceText}" if data.keep_label
|
125
|
+
element.replaceWith(replaceText)
|
126
|
+
|
127
|
+
target.append(element)
|
128
|
+
|
129
|
+
if data.focus
|
130
|
+
element.focus()
|
131
|
+
|
132
|
+
checkbox: (data) ->
|
133
|
+
target = @__parent.check_target(data)
|
134
|
+
element = $("<label class='inline-checkbox'><input type='checkbox'><span>#{@__parent.escape(data.label,data)}</span></label>'")
|
135
|
+
if data.value
|
136
|
+
element.find('input').attr "checked", true
|
137
|
+
element.addClass "checked"
|
138
|
+
element.change (e) =>
|
139
|
+
element.toggleClass("checked", element.find('input').prop('checked'))
|
140
|
+
@__parent.send({
|
141
|
+
id:data.id
|
142
|
+
action:'callback'
|
143
|
+
source:'input'
|
144
|
+
checked: element.find('input').prop('checked')
|
145
|
+
original_msg:data
|
146
|
+
})
|
147
|
+
element.click (e) =>
|
148
|
+
if e.shiftKey and @__lastChecked
|
149
|
+
all_boxes = $('.inline-checkbox')
|
150
|
+
start = all_boxes.index(@__lastChecked)
|
151
|
+
stop = all_boxes.index(element)
|
152
|
+
console.log start, stop
|
153
|
+
|
154
|
+
all_boxes.slice(Math.min(start, stop), Math.max(start, stop) + 1).find('input').prop("checked", @__lastChecked.find('input').prop("checked"))
|
155
|
+
all_boxes.change()
|
156
|
+
else
|
157
|
+
@__lastChecked = element
|
158
|
+
target.append(element)
|
159
|
+
|
160
|
+
alert: (data) ->
|
161
|
+
alert(data.text)
|
162
|
+
|
163
|
+
title: (data) ->
|
164
|
+
document.title = data.title
|
165
|
+
|
166
|
+
status: (data) ->
|
167
|
+
@__parent.status.show_status(data)
|
168
|
+
|
169
|
+
layout: (data) ->
|
170
|
+
$("body").html(data.data)
|
171
|
+
|
172
|
+
script: (data) ->
|
173
|
+
eval(data.data)
|
174
|
+
|
175
|
+
table: (data) ->
|
176
|
+
target = @__parent.check_target(data)
|
177
|
+
html = "<table>"
|
178
|
+
if data.headers
|
179
|
+
html += "<tr>"
|
180
|
+
html += "<th>#{@__parent.escape(header, data)}</th>" for header in data.headers
|
181
|
+
html += "<tr>"
|
182
|
+
for row in data.rows
|
183
|
+
html += "<tr>"
|
184
|
+
html += "<td>#{@__parent.escape(cell, data)}</td>" for cell in row
|
185
|
+
html += "</tr>"
|
186
|
+
html += "</table>"
|
187
|
+
html = $(html)
|
188
|
+
unless data.interactive is false
|
189
|
+
html.delegate 'td', 'mouseover mouseout', ->
|
190
|
+
pos = $(this).index()
|
191
|
+
html.find("td:nth-child(#{(pos+1)})").toggleClass("hover")
|
192
|
+
@__parent.add(html, target, data)
|
@@ -0,0 +1,43 @@
|
|
1
|
+
#= require websocket.coffee
|
2
|
+
__geosearch = null
|
3
|
+
$.extend WSClient.prototype.actions,
|
4
|
+
map: (data) ->
|
5
|
+
default_options =
|
6
|
+
latitude: 51.505
|
7
|
+
longitude: -0.09
|
8
|
+
zoom: 13
|
9
|
+
marker: true
|
10
|
+
replace: true
|
11
|
+
|
12
|
+
options = $.extend default_options, data
|
13
|
+
|
14
|
+
if options.address
|
15
|
+
__geosearch ||= new L.GeoSearch.Provider.OpenStreetMap()
|
16
|
+
url = __geosearch.GetServiceUrl(options.address)
|
17
|
+
$.getJSON url, (json) =>
|
18
|
+
console.log(json)
|
19
|
+
options.latitude = json[0].lat
|
20
|
+
options.longitude = json[0].lon
|
21
|
+
options.address = null
|
22
|
+
@map(options)
|
23
|
+
return
|
24
|
+
|
25
|
+
target = @__parent.check_target(data)
|
26
|
+
mapDiv = $('<div id="map" class="map"></div>')
|
27
|
+
@__parent.add(mapDiv, target, data)
|
28
|
+
mapDiv.height target.height()
|
29
|
+
$(window).resize ->
|
30
|
+
mapDiv.height target.height()
|
31
|
+
|
32
|
+
latitude = options.latitude
|
33
|
+
longitude = options.longitude
|
34
|
+
|
35
|
+
map = L.map('map', {center: [latitude, longitude], zoom: options.zoom})
|
36
|
+
L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
37
|
+
attribution: 'Map data © <a href="http://openstreetmap.org">OpenStreetMap</a> contributors, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>',
|
38
|
+
maxZoom: 19,
|
39
|
+
subdomans: ["a.tile", "b.tile", "c.tile"]
|
40
|
+
}).addTo(map);
|
41
|
+
|
42
|
+
L.Icon.Default.imagePath = "/images"
|
43
|
+
L.marker([latitude, longitude]).addTo(map) if options.marker
|
@@ -0,0 +1,233 @@
|
|
1
|
+
$.event.special.removed =
|
2
|
+
remove: (o) ->
|
3
|
+
o.handler() if o.handler
|
4
|
+
|
5
|
+
$.extend WSClient.prototype.actions,
|
6
|
+
plot: (data) ->
|
7
|
+
target = @__parent.check_target(data)
|
8
|
+
@__plots ||= {}
|
9
|
+
if @__plots[data.id]
|
10
|
+
@__plots[data.id].update(data)
|
11
|
+
else
|
12
|
+
@__plots[data.id] = new Plot(target, @__parent, data)
|
13
|
+
|
14
|
+
class Plot
|
15
|
+
default_options:
|
16
|
+
color: "#fff"
|
17
|
+
replace: true
|
18
|
+
size: 1.0
|
19
|
+
orientation: 'vertical'
|
20
|
+
xscale: 1.0
|
21
|
+
yscale: 1.0
|
22
|
+
tick_height: 15
|
23
|
+
number_of_ticks: 10
|
24
|
+
tick_color: "rgba(255,255,255,0.5)"
|
25
|
+
tick_precision: 2
|
26
|
+
id: "spec0"
|
27
|
+
draw_zero: true
|
28
|
+
fill_color: null
|
29
|
+
draw_line: true
|
30
|
+
draw_marker: false
|
31
|
+
marker_color: '#88F'
|
32
|
+
|
33
|
+
parse_property: (prop) ->
|
34
|
+
parseInt(prop.slice(0, -1))
|
35
|
+
|
36
|
+
move_zoom: (event) =>
|
37
|
+
@zoom_box.width = event.pageX - @zoom_box.left
|
38
|
+
@zoom_box.height = event.pageY - @zoom_box.top
|
39
|
+
@zoom_element.css "width", "#{Math.abs(@zoom_box.width)}"
|
40
|
+
@zoom_element.css "height", "#{Math.abs(@zoom_box.height)}"
|
41
|
+
|
42
|
+
@zoom_element.css "left", event.pageX if @zoom_box.width < 0
|
43
|
+
@zoom_element.css "top", event.pageY if @zoom_box.height < 0
|
44
|
+
|
45
|
+
mousemove: (event) =>
|
46
|
+
@mouse_element.removeClass "hidden"
|
47
|
+
@canvas.focus()
|
48
|
+
relative_x_pos = (event.pageX - @canvas.offset().left) / @plot_width
|
49
|
+
relative_y_pos = 1.0 - ((event.pageY - @canvas.offset().top) / @plot_height)
|
50
|
+
|
51
|
+
@mouse_element.css "left", "#{event.pageX + 15}px"
|
52
|
+
@mouse_element.css "top", "#{event.pageY + 15}px"
|
53
|
+
|
54
|
+
mouse_x_value = relative_x_pos * @xscaled_end_value + (1.0 - relative_x_pos) * @xscaled_start_value
|
55
|
+
mouse_x_value = mouse_x_value * @xvalue_scale
|
56
|
+
mouse_y_value = relative_y_pos * @yscaled_end_value + (1.0 - relative_y_pos) * @yscaled_start_value
|
57
|
+
@mouse_element.text "#{mouse_x_value.toFixed(2)}. #{mouse_y_value.toFixed(2)}"
|
58
|
+
|
59
|
+
@move_zoom(event) if @mouse_is_down
|
60
|
+
|
61
|
+
mousedown: (event) =>
|
62
|
+
unless @mouse_is_down
|
63
|
+
@zoom_box ||= {}
|
64
|
+
@zoom_box.left = event.pageX
|
65
|
+
@zoom_box.top = event.pageY
|
66
|
+
@zoom_element.css "left", event.pageX
|
67
|
+
@zoom_element.css "top", event.pageY
|
68
|
+
@zoom_element.css "width", 0
|
69
|
+
@zoom_element.css "height", 0
|
70
|
+
@zoom_element.removeClass("hidden")
|
71
|
+
@mouse_is_down = true
|
72
|
+
|
73
|
+
mouseup: (event) =>
|
74
|
+
@mouse_is_down = false
|
75
|
+
@zoom_element.addClass "hidden"
|
76
|
+
|
77
|
+
zoom_width = @parse_property(@zoom_element.css "width")
|
78
|
+
zoom_xstart_pixel = @parse_property(@zoom_element.css "left") - @canvas.offset().left
|
79
|
+
zoom_xend_pixel = zoom_xstart_pixel + zoom_width
|
80
|
+
|
81
|
+
relative_xstart_pos = zoom_xstart_pixel / @plot_width
|
82
|
+
relative_xend_pos = zoom_xend_pixel / @plot_width
|
83
|
+
|
84
|
+
zoom_xstart_value = relative_xstart_pos * @xscaled_end_value + (1.0 - relative_xstart_pos) * @xscaled_start_value
|
85
|
+
zoom_xend_value = relative_xend_pos * @xscaled_end_value + (1.0 - relative_xend_pos) * @xscaled_start_value
|
86
|
+
|
87
|
+
zoom_height = @parse_property(@zoom_element.css "height")
|
88
|
+
zoom_yend_pixel = @parse_property(@zoom_element.css "top") - @canvas.offset().top
|
89
|
+
zoom_ystart_pixel = zoom_yend_pixel + zoom_height
|
90
|
+
|
91
|
+
relative_ystart_pos = 1.0 - ((zoom_ystart_pixel) / @plot_height)
|
92
|
+
relative_yend_pos = 1.0 - ((zoom_yend_pixel) / @plot_height)
|
93
|
+
|
94
|
+
zoom_ystart_value = relative_ystart_pos * @yscaled_end_value + (1.0 - relative_ystart_pos) * @yscaled_start_value
|
95
|
+
zoom_yend_value = relative_yend_pos * @yscaled_end_value + (1.0 - relative_yend_pos) * @yscaled_start_value
|
96
|
+
|
97
|
+
console.log zoom_ystart_value, zoom_yend_value
|
98
|
+
|
99
|
+
@zoom_to(zoom_xstart_value, zoom_xend_value, zoom_ystart_value, zoom_yend_value)
|
100
|
+
|
101
|
+
keypress: (event) =>
|
102
|
+
switch event.which
|
103
|
+
when 114, 97
|
104
|
+
@zoom_to(@xstart_value, @xend_value, @ystart_value, @yend_value)
|
105
|
+
@draw(@data)
|
106
|
+
|
107
|
+
setup_hover: ->
|
108
|
+
@zoom_element = $('#plot-zoombox')
|
109
|
+
if @zoom_element.size() is 0
|
110
|
+
@zoom_element = $('<div id="plot-zoom-element" class="hidden"></div>')
|
111
|
+
$('body').append @zoom_element
|
112
|
+
|
113
|
+
@mouse_element = $('#plot-mouseover')
|
114
|
+
if @mouse_element.size() is 0
|
115
|
+
@mouse_element = $('<div id="plot-mouseover" class="hidden"></div>')
|
116
|
+
$('body').append(@mouse_element)
|
117
|
+
|
118
|
+
@canvas.mousemove @mousemove
|
119
|
+
@canvas.mouseout (event) => @mouse_element.addClass "hidden"
|
120
|
+
@canvas.mousedown @mousedown
|
121
|
+
@canvas.mouseup @mouseup
|
122
|
+
@canvas.keypress @keypress
|
123
|
+
@canvas.on 'removed', => @canvas.off()
|
124
|
+
|
125
|
+
create_container: ->
|
126
|
+
@container = $("<div class='plot' id='#{@id}'><canvas tabindex='1'></canvas></div>")
|
127
|
+
@__parent.add @container, @target, @options
|
128
|
+
|
129
|
+
set_canvas_size: ->
|
130
|
+
@container_width = @container.width()
|
131
|
+
@container_height = @container.height()
|
132
|
+
@canvas[0].width = @canvas_width = @container_width
|
133
|
+
@canvas[0].height = @canvas_height = @container_height
|
134
|
+
@set_scale_values()
|
135
|
+
|
136
|
+
zoom_to: (@xscaled_start_value, @xscaled_end_value, @yscaled_start_value, @yscaled_end_value) ->
|
137
|
+
@draw(@data)
|
138
|
+
|
139
|
+
set_scale_values: ->
|
140
|
+
@xvalue_scale = @options.xscale
|
141
|
+
@xend_value = @options.xend || (@data.length - 1)
|
142
|
+
@xstart_value = @options.xstart || 0
|
143
|
+
|
144
|
+
@yvalue_scale = @options.yscale
|
145
|
+
@ystart_value = @options.ystart || 0
|
146
|
+
@ystart_value = Math.min(@data...) if @ystart_value is 'min'
|
147
|
+
@yend_value = @options.yend || Math.max(@data...)
|
148
|
+
|
149
|
+
@plot_height = @canvas_height - @options.tick_height
|
150
|
+
@plot_width = @canvas_width
|
151
|
+
|
152
|
+
@xscaled_start_value = @xstart_value
|
153
|
+
@xscaled_end_value = @xend_value
|
154
|
+
|
155
|
+
@yscaled_start_value = @ystart_value
|
156
|
+
@yscaled_end_value = @yend_value
|
157
|
+
|
158
|
+
y_value_to_pixel: (y) ->
|
159
|
+
@plot_height - (y - @yscaled_start_value) / (@yscaled_end_value - @yscaled_start_value) * @plot_height
|
160
|
+
|
161
|
+
x_value_to_pixel: (i) ->
|
162
|
+
(i - @xscaled_start_value) / (@xscaled_end_value - @xscaled_start_value) * @plot_width
|
163
|
+
|
164
|
+
draw: (data) ->
|
165
|
+
@ctx.clearRect 0, 0, @canvas_width, @canvas_height
|
166
|
+
@ctx.fillStyle = @options.marker_color
|
167
|
+
@ctx.strokeStyle = @options.color
|
168
|
+
|
169
|
+
@ctx.font = "8px Monospace";
|
170
|
+
@ctx.textAlign = "center";
|
171
|
+
|
172
|
+
@ctx.beginPath()
|
173
|
+
@ctx.moveTo(@x_value_to_pixel(0), @y_value_to_pixel(data[0]))
|
174
|
+
for i in [1..data.length - 1]
|
175
|
+
@ctx.lineTo(@x_value_to_pixel(i), @y_value_to_pixel(data[i])) if @options.draw_line
|
176
|
+
@ctx.fillText("x", @x_value_to_pixel(i), @y_value_to_pixel(data[i])) if @options.draw_marker
|
177
|
+
@ctx.moveTo(@x_value_to_pixel(0), @y_value_to_pixel(data[0]))
|
178
|
+
@ctx.closePath()
|
179
|
+
|
180
|
+
@ctx.fillStyle = @options.fill_color
|
181
|
+
@ctx.fill() if @options.fill_color
|
182
|
+
@ctx.stroke()
|
183
|
+
@draw_ticks()
|
184
|
+
|
185
|
+
draw_ticks: ->
|
186
|
+
@ctx.font = "#{@options.tick_height - 3}px Monospace";
|
187
|
+
@ctx.fillStyle = @options.tick_color
|
188
|
+
@ctx.strokeStyle = @options.tick_color
|
189
|
+
@ctx.textAlign = "center";
|
190
|
+
@ctx.textBaseline = 'middle';
|
191
|
+
number_of_ticks = @options.number_of_ticks + 1
|
192
|
+
tick_height = @options.tick_height
|
193
|
+
for i in [1..number_of_ticks - 1]
|
194
|
+
@ctx.beginPath()
|
195
|
+
pos = i * @plot_width / number_of_ticks
|
196
|
+
@ctx.moveTo(pos, @plot_height + tick_height / 2)
|
197
|
+
@ctx.lineTo(pos, @plot_height - tick_height)
|
198
|
+
@ctx.stroke()
|
199
|
+
|
200
|
+
tickStart = @xscaled_start_value
|
201
|
+
tickEnd = @xscaled_end_value
|
202
|
+
tickValue = i * 1.0 / number_of_ticks
|
203
|
+
tickValue = tickValue * tickEnd + (1.0 - tickValue) * tickStart
|
204
|
+
tickValue = tickValue * @xvalue_scale
|
205
|
+
@ctx.textAlign = 'start'
|
206
|
+
@ctx.fillText("#{tickValue.toFixed(@options.tick_precision)}", pos + 3, @canvas_height - 3)
|
207
|
+
|
208
|
+
@ctx.beginPath()
|
209
|
+
@ctx.moveTo(0, @plot_height)
|
210
|
+
@ctx.lineTo(@canvas_width, @plot_height)
|
211
|
+
@ctx.stroke()
|
212
|
+
|
213
|
+
update: (data) ->
|
214
|
+
@data = data.data
|
215
|
+
$.extend @options, data
|
216
|
+
@draw(data.data)
|
217
|
+
|
218
|
+
constructor: (@target, @__parent, @input_options) ->
|
219
|
+
@options = $.extend({}, @default_options, @input_options)
|
220
|
+
@id = @options.id
|
221
|
+
@data = @options.data
|
222
|
+
|
223
|
+
@container = @target.find("##{@id}")
|
224
|
+
|
225
|
+
if @container.size() is 0
|
226
|
+
@create_container()
|
227
|
+
|
228
|
+
@canvas = @container.find("canvas")
|
229
|
+
@ctx = @canvas[0].getContext("2d")
|
230
|
+
@set_canvas_size()
|
231
|
+
@setup_hover()
|
232
|
+
|
233
|
+
@draw(@data)
|