visage-app 0.3.3 → 0.9.0.pre1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENCE +27 -0
- data/README.md +11 -0
- data/VERSION +1 -1
- data/lib/visage-app.rb +2 -10
- data/lib/visage-app/collectd/json.rb +11 -42
- data/lib/visage-app/profile.rb +7 -3
- data/lib/visage-app/public/images/active.png +0 -0
- data/lib/visage-app/public/images/loader.gif +0 -0
- data/lib/visage-app/public/javascripts/graph.js +409 -450
- data/lib/visage-app/public/javascripts/highcharts.js +135 -0
- data/lib/visage-app/public/javascripts/highcharts.src.js +8724 -0
- data/lib/visage-app/public/javascripts/keyboard.js +151 -0
- data/lib/visage-app/public/javascripts/mootools-1.2.5.1-more.js +350 -0
- data/lib/visage-app/public/stylesheets/screen.css +68 -19
- data/lib/visage-app/views/layout.haml +16 -6
- data/lib/visage-app/views/profile.haml +1 -5
- data/lib/visage-app/views/profiles.haml +3 -3
- metadata +18 -17
- data/lib/visage-app/config/fallback-colors.yaml +0 -82
- data/lib/visage-app/config/plugin-colors.yaml +0 -60
- data/lib/visage-app/public/javascripts/g.line-min.js +0 -7
- data/lib/visage-app/public/javascripts/g.line.js +0 -218
- data/lib/visage-app/public/javascripts/g.raphael-min.js +0 -7
- data/lib/visage-app/public/javascripts/g.raphael.js +0 -475
- data/lib/visage-app/public/javascripts/mootools-1.2.3.1-more.js +0 -104
- data/lib/visage-app/public/javascripts/raphael-min.js +0 -113
- data/lib/visage-app/public/javascripts/raphael.js +0 -3395
data/LICENCE
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
Copyright (c) 2009-2010 Lindsay Holmwood
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
20
|
+
|
21
|
+
Visage is distributed with Highcharts. Torstein Hønsi has kindly granted
|
22
|
+
permission to distribute Highcharts under the GPL as part of Visage.
|
23
|
+
|
24
|
+
If you ever need an excellent JavaScript charting library, please consider
|
25
|
+
purchasing a [commercial license](http://highcharts.com/license) for
|
26
|
+
Highcharts.
|
27
|
+
|
data/README.md
CHANGED
@@ -152,3 +152,14 @@ Run all cucumber features:
|
|
152
152
|
|
153
153
|
$ rake cucumber
|
154
154
|
|
155
|
+
Licencing
|
156
|
+
---------
|
157
|
+
|
158
|
+
Visage is MIT licensed.
|
159
|
+
|
160
|
+
Visage is distributed with Highcharts. Torstein Hønsi has kindly granted
|
161
|
+
permission to distribute Highcharts under the GPL as part of Visage.
|
162
|
+
|
163
|
+
If you ever need an excellent JavaScript charting library, please consider
|
164
|
+
purchasing a [commercial license](http://highcharts.com/license) for
|
165
|
+
Highcharts.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.9.0.pre1
|
data/lib/visage-app.rb
CHANGED
@@ -25,13 +25,7 @@ module Visage
|
|
25
25
|
|
26
26
|
configure do
|
27
27
|
Visage::Config.use do |c|
|
28
|
-
# Base configuration files.
|
29
|
-
c['profiles'] = Visage::Config::File.load('profiles.yaml', :create => true, :ignore_bundled => true)
|
30
|
-
c['plugin_colors'] = Visage::Config::File.load('plugin-colors.yaml')
|
31
|
-
c['fallback_colors'] = Visage::Config::File.load('fallback-colors.yaml')
|
32
|
-
|
33
28
|
# FIXME: make this configurable through file
|
34
|
-
c['shade'] = false
|
35
29
|
c['rrddir'] = ENV["RRDDIR"] ? Pathname.new(ENV["RRDDIR"]).expand_path : Pathname.new("/var/lib/collectd/rrd").expand_path
|
36
30
|
end
|
37
31
|
end
|
@@ -96,14 +90,12 @@ module Visage
|
|
96
90
|
plugin = params[:captures][1].gsub("\0", "")
|
97
91
|
plugin_instances = params[:captures][2].gsub("\0", "")
|
98
92
|
|
99
|
-
collectd = CollectdJSON.new(:rrddir => Visage::Config.rrddir
|
100
|
-
:fallback_colors => Visage::Config.fallback_colors)
|
93
|
+
collectd = CollectdJSON.new(:rrddir => Visage::Config.rrddir)
|
101
94
|
json = collectd.json(:host => host,
|
102
95
|
:plugin => plugin,
|
103
96
|
:plugin_instances => plugin_instances,
|
104
97
|
:start => params[:start],
|
105
|
-
:finish => params[:finish]
|
106
|
-
:plugin_colors => Visage::Config.plugin_colors)
|
98
|
+
:finish => params[:finish])
|
107
99
|
# if the request is cross-domain, we need to serve JSONP
|
108
100
|
maybe_wrap_with_callback(json)
|
109
101
|
end
|
@@ -9,14 +9,11 @@ require 'yajl'
|
|
9
9
|
# Exposes RRDs as JSON.
|
10
10
|
#
|
11
11
|
# A loose shim onto RRDtool, with some extra logic to normalise the data.
|
12
|
-
# Also provides a recommended color for rendering the data in a line graph.
|
13
12
|
#
|
14
13
|
class CollectdJSON
|
15
14
|
|
16
15
|
def initialize(opts={})
|
17
16
|
@rrddir = opts[:rrddir] || CollectdJSON.rrddir
|
18
|
-
@fallback_colors = opts[:fallback_colors] || {}
|
19
|
-
@used_fallbacks = []
|
20
17
|
end
|
21
18
|
|
22
19
|
# Entry point.
|
@@ -25,7 +22,6 @@ class CollectdJSON
|
|
25
22
|
plugin = opts[:plugin]
|
26
23
|
plugin_instances = opts[:plugin_instances][/\w.*/]
|
27
24
|
instances = plugin_instances.blank? ? '*' : '{' + plugin_instances.split('/').join(',') + '}'
|
28
|
-
@colors = opts[:plugin_colors]
|
29
25
|
@plugin_names = []
|
30
26
|
|
31
27
|
rrdglob = "#{@rrddir}/#{host}/#{plugin}/#{instances}.rrd"
|
@@ -66,18 +62,20 @@ class CollectdJSON
|
|
66
62
|
# the same file). Separate the metrics.
|
67
63
|
rrd_data.each_pair do |source, metric|
|
68
64
|
|
69
|
-
#
|
65
|
+
# Filter out NaNs and weirdly massive values so yajl doesn't choke
|
70
66
|
metric.map! do |datapoint|
|
71
|
-
|
67
|
+
case
|
68
|
+
when datapoint && datapoint.nan?
|
69
|
+
@tripped = true
|
70
|
+
@last_valid
|
71
|
+
when @tripped
|
72
|
+
@last_valid
|
73
|
+
else
|
74
|
+
@last_valid = datapoint
|
75
|
+
end
|
72
76
|
end
|
73
77
|
|
74
|
-
|
75
|
-
metric.slice!(-1)
|
76
|
-
|
77
|
-
color = color_for(:host => data[:host],
|
78
|
-
:plugin => data[:plugin],
|
79
|
-
:instance => data[:instance],
|
80
|
-
:metric => source)
|
78
|
+
metric[-1] = 0.0
|
81
79
|
|
82
80
|
structure[data[:host]] ||= {}
|
83
81
|
structure[data[:host]][data[:plugin]] ||= {}
|
@@ -86,7 +84,6 @@ class CollectdJSON
|
|
86
84
|
structure[data[:host]][data[:plugin]][data[:instance]][source][:start] ||= data[:start]
|
87
85
|
structure[data[:host]][data[:plugin]][data[:instance]][source][:finish] ||= data[:finish]
|
88
86
|
structure[data[:host]][data[:plugin]][data[:instance]][source][:data] ||= metric
|
89
|
-
structure[data[:host]][data[:plugin]][data[:instance]][source][:color] ||= color
|
90
87
|
end
|
91
88
|
end
|
92
89
|
|
@@ -94,34 +91,6 @@ class CollectdJSON
|
|
94
91
|
encoder.encode(structure)
|
95
92
|
end
|
96
93
|
|
97
|
-
# We append the recommended line color onto data set, so the javascript
|
98
|
-
# doesn't try and have to work it out. This lets us use all sorts of funky
|
99
|
-
# fallback logic when determining what colours should be used.
|
100
|
-
def color_for(opts={})
|
101
|
-
|
102
|
-
plugin = opts[:plugin]
|
103
|
-
instance = opts[:instance]
|
104
|
-
metric = opts[:metric]
|
105
|
-
|
106
|
-
return fallback_color unless plugin
|
107
|
-
return color_for(opts.merge(:plugin => plugin[/(.+)-.+$/, 1])) unless @colors[plugin]
|
108
|
-
return fallback_color unless instance
|
109
|
-
return color_for(opts.merge(:instance => instance[/(.+)-.+$/, 1])) unless @colors[plugin][instance]
|
110
|
-
return @colors[plugin][instance][metric] if @colors[plugin][instance]
|
111
|
-
return fallback_color
|
112
|
-
end
|
113
|
-
|
114
|
-
def fallback_color
|
115
|
-
fallbacks = @fallback_colors.to_a.sort_by {|pair| pair[1]['fallback_order'] }
|
116
|
-
fallback = fallbacks.find { |color| !@used_fallbacks.include?(color) }
|
117
|
-
unless fallback
|
118
|
-
@used_fallbacks = []
|
119
|
-
fallback = fallbacks.find { |color| !@used_fallbacks.include?(color) }
|
120
|
-
end
|
121
|
-
@used_fallbacks << fallback
|
122
|
-
fallback[1]['color'] || "#000"
|
123
|
-
end
|
124
|
-
|
125
94
|
class << self
|
126
95
|
attr_writer :rrddir
|
127
96
|
|
data/lib/visage-app/profile.rb
CHANGED
@@ -11,15 +11,19 @@ module Visage
|
|
11
11
|
attr_reader :options, :selected_hosts, :hosts, :selected_metrics, :metrics,
|
12
12
|
:name, :errors
|
13
13
|
|
14
|
+
def self.load
|
15
|
+
Visage::Config::File.load('profiles.yaml', :create => true, :ignore_bundled => true) || {}
|
16
|
+
end
|
17
|
+
|
14
18
|
def self.get(id)
|
15
19
|
url = id.downcase.gsub(/[^\w]+/, "+")
|
16
|
-
profiles =
|
20
|
+
profiles = self.load
|
17
21
|
profiles[url] ? self.new(profiles[url]) : nil
|
18
22
|
end
|
19
23
|
|
20
24
|
def self.all(opts={})
|
21
25
|
sort = opts[:sort]
|
22
|
-
profiles =
|
26
|
+
profiles = self.load
|
23
27
|
profiles = sort == "name" ? profiles.sort.map {|i| i.last } : profiles.values
|
24
28
|
profiles.map { |prof| self.new(prof) }
|
25
29
|
end
|
@@ -63,7 +67,7 @@ module Visage
|
|
63
67
|
:url => @options[:profile_name].downcase.gsub(/[^\w]+/, "+") }
|
64
68
|
|
65
69
|
# Save it.
|
66
|
-
profiles =
|
70
|
+
profiles = self.load
|
67
71
|
profiles[attrs[:url]] = attrs
|
68
72
|
|
69
73
|
Visage::Config::File.open('profiles.yaml') do |file|
|
Binary file
|
Binary file
|
@@ -1,3 +1,127 @@
|
|
1
|
+
function formatSeriesLabel(labels) {
|
2
|
+
var host = labels[0],
|
3
|
+
plugin = labels[1],
|
4
|
+
instance = labels[2],
|
5
|
+
metric = labels[3],
|
6
|
+
name;
|
7
|
+
|
8
|
+
// Generic label building
|
9
|
+
name = instance
|
10
|
+
name = name.replace(plugin, '')
|
11
|
+
name = name.replace(plugin.split('-')[0], '')
|
12
|
+
name = name.replace('tcp_connections', '')
|
13
|
+
name = name.replace('ps_state', '')
|
14
|
+
name += metric == "value" ? "" : " (" + metric + ")"
|
15
|
+
name = name.replace(/^[-|_]*/, '')
|
16
|
+
name = name.trim().replace(/^\((.*)\)$/, '$1')
|
17
|
+
name = plugin == "irq" ? name.replace(/^/, 'irq ') : ''
|
18
|
+
|
19
|
+
// Plugin specific labeling
|
20
|
+
if (plugin == "interface") {
|
21
|
+
name += instance.replace(/^if_(.*)-(.*)/, '$2 $1') + ' (' + metric + ')'
|
22
|
+
}
|
23
|
+
if (["processes", "memory"].contains(plugin) || plugin.test(/^cpu-\d+/) ) {
|
24
|
+
name += instance.split('-')[1]
|
25
|
+
}
|
26
|
+
if (plugin == "swap") {
|
27
|
+
if (instance.test(/^swap_io/)) {
|
28
|
+
name += instance.replace(/^swap_(\w*)-(.*)$/, '$1 $2')
|
29
|
+
}
|
30
|
+
if (instance.test(/^swap-/)) {
|
31
|
+
name += instance.split('-')[1]
|
32
|
+
}
|
33
|
+
}
|
34
|
+
if (plugin == "load") {
|
35
|
+
name += metric.replace(/^\((.*)\)$/, '$1')
|
36
|
+
}
|
37
|
+
if (plugin.test(/^disk/)) {
|
38
|
+
name += instance.replace(/^disk_/, '') + ' (' + metric + ')'
|
39
|
+
}
|
40
|
+
if (["entropy","users"].contains(plugin)) {
|
41
|
+
name += metric
|
42
|
+
}
|
43
|
+
if (plugin == "uptime") {
|
44
|
+
name += instance
|
45
|
+
}
|
46
|
+
if (plugin == "ping") {
|
47
|
+
if (instance.test(/^ping_/)) {
|
48
|
+
name += instance.replace(/^ping_(.*)-(.*)$/, '$1 $2')
|
49
|
+
} else {
|
50
|
+
name += metric + ' ' + instance.split('-')[1]
|
51
|
+
}
|
52
|
+
}
|
53
|
+
if (plugin == "vmem") {
|
54
|
+
if (instance.test(/^vmpage_number-/)) {
|
55
|
+
name += instance.replace(/^vmpage_number-(.*)$/, '$1').replace('_', ' ')
|
56
|
+
}
|
57
|
+
if (instance.test(/^vmpage_io/)) {
|
58
|
+
name += instance.replace(/^vmpage_io-(.*)$/, '$1 ') + metric
|
59
|
+
}
|
60
|
+
if (instance.test(/^vmpage_faults/)) {
|
61
|
+
name += metric.trim() == "minflt" ? 'minor' : 'major'
|
62
|
+
name += ' faults'
|
63
|
+
}
|
64
|
+
}
|
65
|
+
if (plugin.test(/^tcpconns/)) {
|
66
|
+
name += instance.split('-')[1].replace('_', ' ')
|
67
|
+
}
|
68
|
+
if (plugin.test(/^tail/)) {
|
69
|
+
name += plugin.split('-').slice(1).join('-') + ' '
|
70
|
+
name += instance.split('-').slice(1).join('-')
|
71
|
+
}
|
72
|
+
if (plugin == "apache") {
|
73
|
+
var stash = instance.split('_')[1]
|
74
|
+
if (stash.test(/^scoreboard/)) {
|
75
|
+
name += 'connections: ' + stash.split('-')[1]
|
76
|
+
} else {
|
77
|
+
name += stash
|
78
|
+
}
|
79
|
+
|
80
|
+
}
|
81
|
+
return name.trim()
|
82
|
+
}
|
83
|
+
|
84
|
+
function formatValue(value, places) {
|
85
|
+
var places = places ? places : 0
|
86
|
+
switch(true) {
|
87
|
+
case (Math.abs(value) > 1125899906842624):
|
88
|
+
var label = value / 1125899906842624,
|
89
|
+
unit = 'P';
|
90
|
+
break
|
91
|
+
case (Math.abs(value) > 1099511627776):
|
92
|
+
var label = value / 1099511627776,
|
93
|
+
unit = 'T';
|
94
|
+
break
|
95
|
+
case (Math.abs(value) > 1073741824):
|
96
|
+
var label = value / 1073741824,
|
97
|
+
unit = 'G';
|
98
|
+
break
|
99
|
+
case (Math.abs(value) > 1048576):
|
100
|
+
var label = value / 1048576,
|
101
|
+
unit = 'M';
|
102
|
+
break
|
103
|
+
case (Math.abs(value) > 1024):
|
104
|
+
var label = value / 1024,
|
105
|
+
unit = 'K';
|
106
|
+
break
|
107
|
+
default:
|
108
|
+
var label = value,
|
109
|
+
unit = '';
|
110
|
+
break
|
111
|
+
}
|
112
|
+
|
113
|
+
var rounded = label.round(places)
|
114
|
+
|
115
|
+
return rounded + unit
|
116
|
+
}
|
117
|
+
|
118
|
+
function formatDate(d) {
|
119
|
+
var datetime = new Date(d * 1000)
|
120
|
+
return datetime.format("%Y-%m-%d %H:%M:%S UTC%T")
|
121
|
+
}
|
122
|
+
|
123
|
+
|
124
|
+
|
1
125
|
/*
|
2
126
|
* visageBase()
|
3
127
|
*
|
@@ -8,28 +132,15 @@
|
|
8
132
|
var visageBase = new Class({
|
9
133
|
Implements: [Options, Events],
|
10
134
|
options: {
|
11
|
-
width: 900,
|
12
|
-
height: 220,
|
13
|
-
leftEdge: 100,
|
14
|
-
topEdge: 10,
|
15
|
-
gridWidth: 670,
|
16
|
-
gridHeight: 200,
|
17
|
-
columns: 60,
|
18
|
-
rows: 8,
|
19
|
-
gridBorderColour: '#ccc',
|
20
|
-
shade: false,
|
21
135
|
secureJSON: false,
|
22
136
|
httpMethod: 'get',
|
23
|
-
|
137
|
+
live: false
|
24
138
|
},
|
25
139
|
initialize: function(element, host, plugin, options) {
|
26
|
-
this.parentElement = element
|
27
|
-
this.setOptions(options)
|
28
|
-
this.options.host = host
|
29
|
-
this.options.plugin = plugin
|
30
|
-
this.buildGraphHeader();
|
31
|
-
this.buildGraphContainer();
|
32
|
-
this.canvas = Raphael(this.graphContainer, this.options.width, this.options.height);
|
140
|
+
this.parentElement = element
|
141
|
+
this.setOptions(options)
|
142
|
+
this.options.host = host
|
143
|
+
this.options.plugin = plugin
|
33
144
|
data = new Hash()
|
34
145
|
if($chk(this.options.start)) {
|
35
146
|
data.set('start', this.options.start)
|
@@ -37,7 +148,7 @@ var visageBase = new Class({
|
|
37
148
|
if($chk(this.options.finish)) {
|
38
149
|
data.set('finish', this.options.finish)
|
39
150
|
}
|
40
|
-
this.requestData = data
|
151
|
+
this.requestData = data;
|
41
152
|
this.getData(); // calls graphData
|
42
153
|
},
|
43
154
|
dataURL: function() {
|
@@ -72,28 +183,10 @@ var visageBase = new Class({
|
|
72
183
|
|
73
184
|
this.request.send();
|
74
185
|
},
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
'class': 'graph-title',
|
79
|
-
'html': header,
|
80
|
-
'styles': {
|
81
|
-
'color': "#121212"
|
82
|
-
}
|
83
|
-
});
|
84
|
-
$(this.parentElement).grab(this.graphHeader);
|
186
|
+
graphName: function() {
|
187
|
+
name = $chk(this.options.name) ? this.options.name : this.options.plugin
|
188
|
+
return name
|
85
189
|
},
|
86
|
-
buildGraphContainer: function() {
|
87
|
-
$(this.parentElement).set('style', 'padding-top: 1em');
|
88
|
-
|
89
|
-
this.graphContainer = new Element('div', {
|
90
|
-
'class': 'graph container',
|
91
|
-
'styles': {
|
92
|
-
'margin-bottom': '24px'
|
93
|
-
}
|
94
|
-
});
|
95
|
-
$(this.parentElement).grab(this.graphContainer)
|
96
|
-
}
|
97
190
|
});
|
98
191
|
|
99
192
|
|
@@ -111,453 +204,319 @@ var visageGraph = new Class({
|
|
111
204
|
Implements: Chain,
|
112
205
|
// assemble data to graph, then draw it
|
113
206
|
graphData: function(data) {
|
207
|
+
this.response = data
|
208
|
+
this.buildDataStructures()
|
114
209
|
|
115
|
-
this.
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
210
|
+
if ( $defined(this.chart) ) {
|
211
|
+
this.series.each(function(series, index) {
|
212
|
+
this.chart.series[index].setData(series.data)
|
213
|
+
}, this);
|
214
|
+
} else {
|
215
|
+
this.drawChart()
|
216
|
+
}
|
217
|
+
},
|
218
|
+
buildDataStructures: function (data) {
|
219
|
+
var series = this.series = []
|
220
|
+
var host = this.options.host
|
221
|
+
var plugin = this.options.plugin
|
222
|
+
var data = data ? data : this.response
|
122
223
|
|
123
224
|
$each(data[host][plugin], function(instance, iname) {
|
124
225
|
$each(instance, function(metric, mname) {
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
226
|
+
var set = {
|
227
|
+
name: [ host, plugin, iname, mname ],
|
228
|
+
data: metric.data,
|
229
|
+
pointStart: metric.start,
|
230
|
+
pointInterval: (metric.finish - metric.start) / metric.data.length
|
231
|
+
};
|
232
|
+
|
233
|
+
series.push(set)
|
132
234
|
}, this);
|
133
235
|
}, this);
|
134
236
|
|
135
|
-
|
136
|
-
this.drawGraph();
|
137
|
-
|
138
|
-
this.buildLabels();
|
139
|
-
this.addSelectionInterface();
|
140
|
-
this.addDebugInterface();
|
141
|
-
this.buildDateSelector();
|
142
|
-
|
143
|
-
/* disabling this for now for dramatic effect
|
144
|
-
this.buildEmbedder();
|
145
|
-
*/
|
146
|
-
},
|
147
|
-
buildXAxis: function(metric) {
|
148
|
-
var start = metric.start.toInt(),
|
149
|
-
finish = metric.finish.toInt(),
|
150
|
-
length = metric.data.length,
|
151
|
-
interval = (finish - start) / length,
|
152
|
-
counter = start,
|
153
|
-
x = []
|
154
|
-
|
155
|
-
while (counter < finish) {
|
156
|
-
x.push(counter)
|
157
|
-
counter += interval
|
158
|
-
}
|
159
|
-
return x
|
160
|
-
},
|
161
|
-
drawGraph: function() {
|
162
|
-
|
163
|
-
var colors = this.colors;
|
164
|
-
var left = this.options.leftEdge
|
165
|
-
var top = this.options.topEdge
|
166
|
-
var width = this.options.gridWidth
|
167
|
-
var height = this.options.gridHeight
|
168
|
-
var x = this.x // x axis
|
169
|
-
var ys = this.ys // y axes
|
170
|
-
var xstep = x.length / 20
|
171
|
-
var shade = this.options.shade
|
172
|
-
var axis = this.options.axis
|
173
|
-
|
174
|
-
this.canvas.g.txtattr.font = "11px 'sans-serif'";
|
175
|
-
this.graph = this.canvas.g.linechart(left, top, width, height, x, ys, {
|
176
|
-
nostroke: false,
|
177
|
-
width: 1.5,
|
178
|
-
axis: axis,
|
179
|
-
colors: colors,
|
180
|
-
axisxstep: xstep,
|
181
|
-
shade: shade
|
182
|
-
});
|
183
|
-
|
184
|
-
this.formatAxes();
|
237
|
+
return series
|
185
238
|
},
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
/*
|
201
|
-
time.mouseout(function () {
|
202
|
-
this.attr({'text': d.strftime("%H:%M")});
|
203
|
-
});
|
204
|
-
*/
|
205
|
-
});
|
206
|
-
|
207
|
-
$each(this.graph.axis[1].text.items, function (value) {
|
208
|
-
// FIXME: no JS reference on train means awful rounding hacks!
|
209
|
-
// if you are reading this, it's a bug!
|
210
|
-
if (value.attr('text') > 1073741824) {
|
211
|
-
var label = value.attr('text') / 1073741824;
|
212
|
-
var unit = 'g'
|
213
|
-
} else if (value.attr('text') > 1048576) {
|
214
|
-
// and again :-(
|
215
|
-
var label = value.attr('text') / 1048576;
|
216
|
-
var unit = 'm'
|
217
|
-
} else if (value.attr('text') > 1024) {
|
218
|
-
var label = value.attr('text') / 1024;
|
219
|
-
var unit = 'k';
|
220
|
-
} else {
|
221
|
-
var label = value.attr('text');
|
222
|
-
var unit = ''
|
239
|
+
drawChart: function() {
|
240
|
+
var series = this.series,
|
241
|
+
title = this.graphName(),
|
242
|
+
element = this.parentElement,
|
243
|
+
ytitle = this.options.plugin
|
244
|
+
max = 0
|
245
|
+
|
246
|
+
/* Get the maximum value across all sets.
|
247
|
+
* Used later on to determine the decimal place in the label. */
|
248
|
+
series.each(function(set) {
|
249
|
+
var setMax = set.data.max()
|
250
|
+
if ( setMax > max ) {
|
251
|
+
max = setMax
|
223
252
|
}
|
224
|
-
|
225
|
-
var decimal = label.toString().split('.')
|
226
|
-
if ($chk(this.previous) && this.previous.toString()[0] == label.toString()[0] && decimal.length > 1) {
|
227
|
-
var round = '.' + decimal[1][0]
|
228
|
-
} else {
|
229
|
-
var round = ''
|
230
|
-
}
|
231
|
-
|
232
|
-
value.attr({'text': Math.floor(label) + round + unit})
|
233
|
-
this.previous = value.attr('text')
|
234
253
|
});
|
235
254
|
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
'
|
243
|
-
|
244
|
-
|
255
|
+
this.chart = new Highcharts.Chart({
|
256
|
+
chart: {
|
257
|
+
renderTo: element,
|
258
|
+
defaultSeriesType: 'line',
|
259
|
+
marginRight: 200,
|
260
|
+
marginBottom: 25,
|
261
|
+
zoomType: 'xy',
|
262
|
+
height: 300,
|
263
|
+
events: {
|
264
|
+
load: function(e) {
|
265
|
+
setInterval(function() {
|
266
|
+
if (this.options.live) {
|
267
|
+
this.getData()
|
268
|
+
}
|
269
|
+
}.bind(this), 10000);
|
270
|
+
}.bind(this)
|
271
|
+
}
|
272
|
+
},
|
273
|
+
title: {
|
274
|
+
text: title,
|
275
|
+
style: {
|
276
|
+
fontSize: '20px',
|
277
|
+
fontWeight: 'bold',
|
278
|
+
color: "#333333"
|
279
|
+
}
|
280
|
+
},
|
281
|
+
xAxis: {
|
282
|
+
type: 'datetime',
|
283
|
+
labels: {
|
284
|
+
y: 20,
|
285
|
+
formatter: function() {
|
286
|
+
var d = new Date(this.value * 1000)
|
287
|
+
return d.format("%H:%M")
|
288
|
+
}
|
289
|
+
},
|
290
|
+
title: {
|
291
|
+
text: null
|
292
|
+
}
|
293
|
+
},
|
294
|
+
yAxis: {
|
295
|
+
title: {
|
296
|
+
text: ytitle
|
297
|
+
},
|
298
|
+
maxPadding: 0,
|
299
|
+
plotLines: [{
|
300
|
+
width: 0.5,
|
301
|
+
}],
|
302
|
+
labels: {
|
303
|
+
formatter: function() {
|
304
|
+
var places = max < 10 ? 2 : 0
|
305
|
+
return formatValue(this.value, places)
|
306
|
+
}
|
307
|
+
}
|
308
|
+
},
|
309
|
+
plotOptions: {
|
310
|
+
series: {
|
311
|
+
stacking: 'normal',
|
312
|
+
marker: {
|
313
|
+
enabled: false
|
314
|
+
},
|
315
|
+
states: {
|
316
|
+
hover: {
|
317
|
+
enabled: true,
|
318
|
+
marker: {
|
319
|
+
symbol: 'triangle'
|
320
|
+
}
|
321
|
+
}
|
322
|
+
}
|
323
|
+
}
|
324
|
+
},
|
325
|
+
tooltip: {
|
326
|
+
formatter: function() {
|
327
|
+
var tip;
|
328
|
+
tip = '<b>' + formatSeriesLabel(this.series.name).trim() + '</b>-> '
|
329
|
+
tip += formatValue(this.y, 2) + ' <br/>'
|
330
|
+
tip += formatDate(this.x)
|
331
|
+
|
332
|
+
return tip
|
333
|
+
}
|
334
|
+
},
|
335
|
+
legend: {
|
336
|
+
layout: 'vertical',
|
337
|
+
align: 'right',
|
338
|
+
verticalAlign: 'top',
|
339
|
+
x: -10,
|
340
|
+
y: 60,
|
341
|
+
borderWidth: 0,
|
342
|
+
itemWidth: 186,
|
343
|
+
labelFormatter: function() {
|
344
|
+
return formatSeriesLabel(this.name)
|
345
|
+
},
|
346
|
+
itemStyle: {
|
347
|
+
cursor: 'pointer',
|
348
|
+
color: '#333333'
|
349
|
+
},
|
350
|
+
itemHoverStyle: {
|
351
|
+
color: '#777777'
|
245
352
|
}
|
246
|
-
});
|
247
|
-
this.embedderContainer.grab(pre);
|
248
|
-
|
249
|
-
var slider = new Fx.Slide(pre, {
|
250
|
-
duration: 200
|
251
|
-
});
|
252
353
|
|
253
|
-
|
354
|
+
},
|
355
|
+
series: series,
|
356
|
+
credits: {
|
357
|
+
enabled: false
|
358
|
+
}
|
359
|
+
});
|
254
360
|
|
255
|
-
|
256
|
-
'id': 'toggler',
|
257
|
-
'class': 'toggler',
|
258
|
-
'html': '(embed)',
|
259
|
-
'href': '#',
|
260
|
-
'styles': {
|
261
|
-
'font-size': '0.7em',
|
262
|
-
}
|
263
|
-
});
|
264
|
-
toggler.addEvent('click', function(e) {
|
265
|
-
e.stop();
|
266
|
-
slider.toggle();
|
267
|
-
});
|
268
|
-
this.embedderTogglerContainer.grab(toggler);
|
269
|
-
},
|
270
|
-
embedCode: function() {
|
271
|
-
baseurl = "{protocol}//{host}".substitute({'host': window.location.host, 'protocol': window.location.protocol});
|
272
|
-
code = "<script src='{baseurl}/javascripts/visage.js' type='text/javascript'></script>".substitute({'baseurl': baseurl});
|
273
|
-
code += "<div id='graph'></div>"
|
274
|
-
code += "<script type='text/javascript'>window.addEvent('domready', function() { var graph = new visageGraph('graph', '{host}', '{plugin}', ".substitute({'host': this.options.host, 'plugin': this.options.plugin});
|
275
|
-
code += "{"
|
276
|
-
code += "width: 900, height: 220, gridWidth: 800, gridHeight: 200, baseurl: '{baseurl}'".substitute({'baseurl': baseurl});
|
277
|
-
code += "}); });</script>"
|
278
|
-
return code.replace('<', '<').replace('>', '>')
|
361
|
+
this.buildDateSelector();
|
279
362
|
},
|
280
|
-
|
281
|
-
var graph = this.graph;
|
363
|
+
buildDateSelector: function() {
|
282
364
|
/*
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
});
|
309
|
-
if (!hasSelected) {
|
310
|
-
var option = new Element('option', {
|
311
|
-
html: 'selected',
|
312
|
-
value: '',
|
313
|
-
selected: true
|
365
|
+
* container
|
366
|
+
* \
|
367
|
+
* - form
|
368
|
+
* \
|
369
|
+
* - select
|
370
|
+
* | \
|
371
|
+
* | - option
|
372
|
+
* | |
|
373
|
+
* | - option
|
374
|
+
* |
|
375
|
+
* - submit
|
376
|
+
*/
|
377
|
+
var currentDate = new Date;
|
378
|
+
var currentUnixTime = parseInt(currentDate.getTime() / 1000);
|
379
|
+
|
380
|
+
var container = $(this.parentElement);
|
381
|
+
var form = new Element('form', {
|
382
|
+
'method': 'get',
|
383
|
+
'events': {
|
384
|
+
'submit': function(e, foo) {
|
385
|
+
e.stop();
|
386
|
+
e.target.getElement('select').getSelected().each(function(option) {
|
387
|
+
data = new Hash()
|
388
|
+
split = option.value.split('=')
|
389
|
+
data.set(split[0], split[1])
|
314
390
|
});
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
if ($chk(graph.selection) && !graph.selectionMade) {
|
321
|
-
var width = this.x - graph.selection.attr('x');
|
322
|
-
graph.selection.attr({'width': width});
|
391
|
+
this.requestData = data
|
392
|
+
|
393
|
+
/* Draw everything again. */
|
394
|
+
this.getData();
|
395
|
+
}.bind(this)
|
323
396
|
}
|
324
397
|
});
|
325
398
|
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
399
|
+
var select = new Element('select', { 'class': 'date timescale' });
|
400
|
+
var timescales = new Hash({ 'hour': 1, '2 hours': 2, '6 hours': 6, '12 hours': 12,
|
401
|
+
'day': 24, '2 days': 48, '3 days': 72,
|
402
|
+
'week': 168, '2 weeks': 336, 'month': 672 });
|
403
|
+
timescales.each(function(hour, label) {
|
404
|
+
var current = this.currentTimePeriod == 'last {label}'.substitute({'label': label });
|
405
|
+
var value = "start={start}".substitute({'start': currentUnixTime - (hour * 3600)});
|
406
|
+
var html = 'last {label}'.substitute({'label': label });
|
407
|
+
|
408
|
+
var option = new Element('option', {
|
409
|
+
html: html,
|
410
|
+
value: value,
|
411
|
+
selected: (current ? 'selected' : '')
|
412
|
+
|
413
|
+
});
|
414
|
+
select.grab(option)
|
337
415
|
});
|
338
|
-
$(this.parentElement).grab(this.embedderTogglerContainer, 'top')
|
339
416
|
|
340
|
-
|
341
|
-
|
417
|
+
var submit = new Element('input', { 'type': 'submit', 'value': 'show' });
|
418
|
+
|
419
|
+
var liveToggler = new Element('input', {
|
420
|
+
'type': 'checkbox',
|
421
|
+
'id': this.parentElement + '-live',
|
422
|
+
'name': 'live',
|
423
|
+
'events': {
|
424
|
+
'click': function() {
|
425
|
+
this.options.live = !this.options.live
|
426
|
+
}.bind(this)
|
427
|
+
},
|
342
428
|
'styles': {
|
343
|
-
'
|
344
|
-
'
|
429
|
+
'margin-left': '4px',
|
430
|
+
'cursor': 'pointer'
|
345
431
|
}
|
346
432
|
});
|
347
|
-
$(this.parentElement).grab(this.timescaleContainer, 'top')
|
348
433
|
|
349
|
-
|
350
|
-
'
|
351
|
-
'
|
434
|
+
var liveLabel = new Element('label', {
|
435
|
+
'for': this.parentElement + '-live',
|
436
|
+
'html': 'Live',
|
352
437
|
'styles': {
|
353
|
-
'
|
354
|
-
'
|
355
|
-
'
|
438
|
+
'font-family': 'sans-serif',
|
439
|
+
'font-size': '11px',
|
440
|
+
'margin-left': '8px',
|
441
|
+
'cursor': 'pointer'
|
356
442
|
}
|
357
443
|
});
|
358
|
-
$(this.parentElement).grab(this.labelsContainer)
|
359
444
|
|
360
|
-
|
361
|
-
'
|
445
|
+
var exportLink = new Element('a', {
|
446
|
+
'href': this.dataURL(),
|
447
|
+
'html': 'Export data',
|
362
448
|
'styles': {
|
363
|
-
'font-
|
364
|
-
'
|
365
|
-
'
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
* \
|
375
|
-
* - form
|
376
|
-
* \
|
377
|
-
* - select
|
378
|
-
* | \
|
379
|
-
* | - option
|
380
|
-
* | |
|
381
|
-
* | - option
|
382
|
-
* |
|
383
|
-
* - submit
|
384
|
-
*/
|
385
|
-
var currentDate = new Date;
|
386
|
-
var currentUnixTime = parseInt(currentDate.getTime() / 1000);
|
387
|
-
|
388
|
-
var container = $(this.timescaleContainer);
|
389
|
-
var form = new Element('form', {
|
390
|
-
'method': 'get',
|
391
|
-
'events': {
|
392
|
-
'submit': function(e, foo) {
|
393
|
-
e.stop();
|
394
|
-
|
395
|
-
/*
|
396
|
-
* Get the selected option, turn it into a hash for
|
397
|
-
* getData() to use.
|
398
|
-
*/
|
399
|
-
data = new Hash()
|
400
|
-
if (e.target.getElement('select').getSelected().get('html') == 'selected') {
|
401
|
-
data.set('start', this.graph.selectionStart);
|
402
|
-
data.set('finish', this.graph.selectionFinish);
|
403
|
-
} else {
|
404
|
-
e.target.getElement('select').getSelected().each(function(option) {
|
405
|
-
split = option.value.split('=')
|
406
|
-
data.set(split[0], split[1])
|
407
|
-
currentTimePeriod = option.get('html') // is this setting a global?
|
408
|
-
}, this);
|
409
|
-
}
|
410
|
-
this.requestData = data
|
411
|
-
|
412
|
-
/* Nuke graph + labels. */
|
413
|
-
this.graph.remove();
|
414
|
-
delete this.x;
|
415
|
-
$(this.labelsContainer).empty();
|
416
|
-
$(this.timescaleContainer).empty();
|
417
|
-
$(this.embedderContainer).empty();
|
418
|
-
$(this.embedderTogglerContainer).empty();
|
419
|
-
if ($defined(this.graph.selection)) {
|
420
|
-
this.graph.selection.remove();
|
421
|
-
}
|
422
|
-
/* Draw everything again. */
|
423
|
-
this.getData();
|
424
|
-
}.bind(this)
|
449
|
+
'font-family': 'sans-serif',
|
450
|
+
'font-size': '11px',
|
451
|
+
'margin-left': '8px',
|
452
|
+
},
|
453
|
+
'events': {
|
454
|
+
'mouseover': function(e) {
|
455
|
+
var url = e.target.get('href')
|
456
|
+
var options = this.requestData.toQueryString()
|
457
|
+
|
458
|
+
if ( options != '' && ! url.contains('?') ) {
|
459
|
+
url += '?' + options
|
425
460
|
}
|
426
|
-
});
|
427
461
|
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
timescales.each(function(hour, label) {
|
433
|
-
var current = this.currentTimePeriod == 'last {label}'.substitute({'label': label });
|
434
|
-
var value = "start={start}".substitute({'start': currentUnixTime - (hour * 3600)});
|
435
|
-
var html = 'last {label}'.substitute({'label': label });
|
436
|
-
|
437
|
-
var option = new Element('option', {
|
438
|
-
html: html,
|
439
|
-
value: value,
|
440
|
-
selected: (current ? 'selected' : '')
|
441
|
-
|
442
|
-
});
|
443
|
-
select.grab(option)
|
444
|
-
});
|
445
|
-
|
446
|
-
var submit = new Element('input', { 'type': 'submit', 'value': 'show' });
|
462
|
+
e.target.set('href', url)
|
463
|
+
}.bind(this)
|
464
|
+
}
|
465
|
+
});
|
447
466
|
|
448
|
-
|
449
|
-
|
450
|
-
|
467
|
+
form.grab(select)
|
468
|
+
form.grab(submit)
|
469
|
+
form.grab(liveToggler)
|
470
|
+
form.grab(liveLabel)
|
471
|
+
form.grab(exportLink)
|
472
|
+
container.grab(form, 'top')
|
451
473
|
},
|
452
|
-
buildLabels: function() {
|
453
|
-
//buildLabels: function(graphLines, instanceNames, dataSources, colors) {
|
454
|
-
|
455
|
-
this.ys.each(function(set, index) {
|
456
|
-
var path = this.graph.lines[index],
|
457
|
-
color = this.colors[index]
|
458
|
-
plugin = this.options.plugin
|
459
|
-
instance = this.instances[index]
|
460
|
-
metric = this.metrics[index]
|
461
|
-
|
462
|
-
var container = new Element('div', {
|
463
|
-
'class': 'label plugin',
|
464
|
-
'styles': {
|
465
|
-
'padding': '0.2em 0.5em 0',
|
466
|
-
'float': 'left',
|
467
|
-
'width': '180px',
|
468
|
-
'font-size': '0.8em'
|
469
|
-
},
|
470
|
-
'events': {
|
471
|
-
'mouseover': function(e) {
|
472
|
-
e.stop();
|
473
|
-
path.animate({'stroke-width': 3}, 300);
|
474
|
-
//path.toFront();
|
475
|
-
},
|
476
|
-
'mouseout': function(e) {
|
477
|
-
e.stop();
|
478
|
-
path.animate({'stroke-width': 1.5}, 300);
|
479
|
-
//path.toBack();
|
480
|
-
},
|
481
|
-
'click': function(e) {
|
482
|
-
e.stop();
|
483
|
-
path.attr('opacity') == 0 ? path.animate({'opacity': 1}, 350) : path.animate({'opacity': 0}, 350);
|
484
|
-
}
|
485
|
-
}
|
486
|
-
});
|
487
|
-
|
488
|
-
var box = new Element('div', {
|
489
|
-
'class': 'label plugin box ' + metric,
|
490
|
-
'html': ' ',
|
491
|
-
'styles': {
|
492
|
-
'background-color': color,
|
493
|
-
'width': '48px',
|
494
|
-
'height': '18px',
|
495
|
-
'float': 'left',
|
496
|
-
'margin-right': '0.5em'
|
497
|
-
}
|
498
|
-
});
|
499
|
-
|
500
|
-
// plugin/instance/metrics names can be unmeaningful. make them pretty
|
501
|
-
var name;
|
502
|
-
name = instance.replace(plugin, '');
|
503
|
-
name = name.replace('tcp_connections', '')
|
504
|
-
name = name.replace('ps_state', '')
|
505
|
-
name = name.replace(plugin.split('-')[0], '')
|
506
|
-
name += metric == "value" ? "" : " (" + metric + ")"
|
507
|
-
name = name.replace(/^[-|_]*/, '')
|
508
|
-
|
509
|
-
var desc = new Element('span', {
|
510
|
-
'class': 'label plugin description ' + metric,
|
511
|
-
'html': name
|
512
|
-
});
|
513
|
-
|
514
|
-
container.grab(box);
|
515
|
-
container.grab(desc);
|
516
|
-
$(this.labelsContainer).grab(container);
|
517
|
-
|
518
|
-
}, this);
|
519
|
-
}
|
520
|
-
})
|
521
474
|
|
522
|
-
var visageSparkline = new Class({
|
523
|
-
Extends: visageGraph,
|
524
|
-
options: {
|
525
|
-
width: 450,
|
526
|
-
height: 80,
|
527
|
-
leftEdge: 1,
|
528
|
-
topEdge: 1,
|
529
|
-
gridWidth: 449,
|
530
|
-
gridHeight: 79,
|
531
|
-
columns: 60,
|
532
|
-
rows: 8,
|
533
|
-
gridBorderColour: '#ccc',
|
534
|
-
shade: false,
|
535
|
-
secureJSON: false,
|
536
|
-
httpMethod: 'get',
|
537
|
-
axis: "0 0 0 0"
|
538
|
-
},
|
539
|
-
graphData: function(data) {
|
540
475
|
|
541
|
-
this.ys = []
|
542
|
-
this.colors = []
|
543
|
-
this.instances = []
|
544
|
-
this.metrics = []
|
545
476
|
|
546
|
-
|
547
|
-
var plugin = this.options.plugin
|
477
|
+
});
|
548
478
|
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
479
|
+
// buildEmbedder: function() {
|
480
|
+
// var pre = new Element('textarea', {
|
481
|
+
// 'id': 'embedder',
|
482
|
+
// 'class': 'embedder',
|
483
|
+
// 'html': this.embedCode(),
|
484
|
+
// 'styles': {
|
485
|
+
// 'width': '500px',
|
486
|
+
// 'padding': '3px'
|
487
|
+
// }
|
488
|
+
// });
|
489
|
+
// this.embedderContainer.grab(pre);
|
490
|
+
//
|
491
|
+
// var slider = new Fx.Slide(pre, {
|
492
|
+
// duration: 200
|
493
|
+
// });
|
494
|
+
//
|
495
|
+
// slider.hide();
|
496
|
+
//
|
497
|
+
// var toggler = new Element('a', {
|
498
|
+
// 'id': 'toggler',
|
499
|
+
// 'class': 'toggler',
|
500
|
+
// 'html': '(embed)',
|
501
|
+
// 'href': '#',
|
502
|
+
// 'styles': {
|
503
|
+
// 'font-size': '0.7em',
|
504
|
+
// }
|
505
|
+
// });
|
506
|
+
// toggler.addEvent('click', function(e) {
|
507
|
+
// e.stop();
|
508
|
+
// slider.toggle();
|
509
|
+
// });
|
510
|
+
// this.embedderTogglerContainer.grab(toggler);
|
511
|
+
// },
|
512
|
+
// embedCode: function() {
|
513
|
+
// baseurl = "{protocol}//{host}".substitute({'host': window.location.host, 'protocol': window.location.protocol});
|
514
|
+
// code = "<script src='{baseurl}/javascripts/visage.js' type='text/javascript'></script>".substitute({'baseurl': baseurl});
|
515
|
+
// code += "<div id='graph'></div>"
|
516
|
+
// code += "<script type='text/javascript'>window.addEvent('domready', function() { var graph = new visageGraph('graph', '{host}', '{plugin}', ".substitute({'host': this.options.host, 'plugin': this.options.plugin});
|
517
|
+
// code += "{"
|
518
|
+
// code += "width: 900, height: 220, gridWidth: 800, gridHeight: 200, baseurl: '{baseurl}'".substitute({'baseurl': baseurl});
|
519
|
+
// code += "}); });</script>"
|
520
|
+
// return code.replace('<', '<').replace('>', '>')
|
521
|
+
// },
|
560
522
|
|
561
|
-
this.drawGraph();
|
562
|
-
}
|
563
|
-
});
|