visage-app 0.1.8 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -1
- data/README.md +5 -12
- data/VERSION +1 -1
- data/bin/visage +2 -1
- data/features/json.feature +30 -19
- data/features/site.feature +5 -3
- data/features/step_definitions/json_steps.rb +55 -11
- data/features/step_definitions/site_steps.rb +9 -2
- data/features/support/env.rb +2 -2
- data/lib/visage/collectd/json.rb +3 -4
- data/lib/visage/collectd/rrds.rb +53 -0
- data/lib/visage/config/init.rb +18 -14
- data/lib/visage/config.ru +4 -2
- data/lib/visage/graph.rb +21 -0
- data/lib/visage/helpers.rb +9 -1
- data/lib/visage/profile.rb +126 -0
- data/lib/visage/public/images/add.png +0 -0
- data/lib/visage/public/images/hosts.png +0 -0
- data/lib/visage/public/images/metrics.png +0 -0
- data/lib/visage/public/images/search.png +0 -0
- data/lib/visage/public/javascripts/g.line-min.js +7 -0
- data/lib/visage/public/javascripts/g.line.js +2 -2
- data/lib/visage/public/javascripts/g.raphael-min.js +7 -0
- data/lib/visage/public/javascripts/g.raphael.js +471 -3
- data/lib/visage/public/javascripts/graph.js +52 -2
- data/lib/visage/public/javascripts/raphael-min.js +109 -3
- data/lib/visage/public/javascripts/raphael.js +714 -534
- data/lib/visage/public/stylesheets/screen.css +172 -15
- data/lib/visage/views/builder.haml +52 -0
- data/lib/visage/views/layout.haml +13 -11
- data/lib/visage/views/profile.haml +18 -0
- data/lib/visage/views/profiles.haml +13 -0
- data/lib/visage-app.rb +92 -57
- metadata +16 -6
- data/lib/visage/config/profiles.yaml +0 -35
- data/lib/visage/views/index.haml +0 -48
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -45,7 +45,6 @@ You can try out the application quickly with:
|
|
45
45
|
|
46
46
|
$ visage start
|
47
47
|
|
48
|
-
|
49
48
|
Configuring
|
50
49
|
-----------
|
51
50
|
|
@@ -91,7 +90,6 @@ Visage can attempt to generate an Apache vhost config for use with Passenger:
|
|
91
90
|
Order allow,deny
|
92
91
|
Allow from all
|
93
92
|
</Directory>
|
94
|
-
|
95
93
|
</VirtualHost>
|
96
94
|
|
97
95
|
Copypasta this into your system's Apache config structure and tune to taste.
|
@@ -123,16 +121,11 @@ Run all cucumber features:
|
|
123
121
|
|
124
122
|
$ rake cucumber
|
125
123
|
|
126
|
-
Specific features:
|
127
|
-
|
128
|
-
$ bin/cucumber --require features/ features/something.feature
|
129
|
-
|
130
124
|
TODO
|
131
125
|
----
|
132
126
|
|
133
|
-
*
|
134
|
-
*
|
135
|
-
*
|
136
|
-
*
|
137
|
-
*
|
138
|
-
* view list of comments
|
127
|
+
* detailed point-in-time data on hover (timestamp, value)
|
128
|
+
* give graph profile an alternate private url
|
129
|
+
* make notes/annotations on private url
|
130
|
+
* include table of axis mappings + default y-axis heights for rendering
|
131
|
+
* view metrics from multiple hosts on the same graph
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
data/bin/visage
CHANGED
@@ -10,7 +10,8 @@ when "start"
|
|
10
10
|
require 'rubygems'
|
11
11
|
require 'rack'
|
12
12
|
config = @root.join('lib/visage/config.ru').to_s
|
13
|
-
Rack::Server.
|
13
|
+
server = Rack::Server.new(:config => config, :Port => 9292)
|
14
|
+
server.start
|
14
15
|
when "genapache"
|
15
16
|
require 'socket'
|
16
17
|
fqdn = Socket.gethostbyname(Socket.gethostname).first
|
data/features/json.feature
CHANGED
@@ -4,63 +4,74 @@ Feature: Export data
|
|
4
4
|
Must be able to extract data
|
5
5
|
From the application
|
6
6
|
|
7
|
+
Scenario: Retreive a list of hosts
|
8
|
+
When I go to /data
|
9
|
+
Then the request should succeed
|
10
|
+
Then I should receive valid JSON
|
11
|
+
And the JSON should have a list of hosts
|
12
|
+
|
13
|
+
Scenario: Retreive a list of hosts
|
14
|
+
When I visit the first available host
|
15
|
+
Then the request should succeed
|
16
|
+
Then I should receive valid JSON
|
17
|
+
And the JSON should have a list of plugins
|
18
|
+
|
7
19
|
Scenario: Retrieve single plugin instance
|
8
|
-
|
20
|
+
Given a list of hosts exist
|
21
|
+
When I visit "memory/memory-free" on the first available host
|
9
22
|
Then the request should succeed
|
10
23
|
Then I should receive valid JSON
|
11
24
|
And the JSON should have a plugin instance named "memory-free"
|
12
25
|
|
13
26
|
Scenario: Retrieve multiple plugin instances
|
14
|
-
|
27
|
+
Given a list of hosts exist
|
28
|
+
When I visit "memory" on the first available host
|
15
29
|
Then the request should succeed
|
16
30
|
Then I should receive valid JSON
|
17
31
|
And the JSON should have a plugin named "memory"
|
18
32
|
And the JSON should have multiple plugin instances under the "memory" plugin
|
19
33
|
|
20
34
|
Scenario: Make cross-domain requests
|
21
|
-
|
35
|
+
Given a list of hosts exist
|
36
|
+
When I visit "cpu-0?callback=foobar" on the first available host
|
22
37
|
Then I should receive JSON wrapped in a callback named "foobar"
|
23
38
|
|
24
39
|
Scenario: Retrieve multiple plugin instances without color definition
|
25
|
-
|
40
|
+
Given a list of hosts exist
|
41
|
+
When I visit "memory" on the first available host
|
26
42
|
Then the request should succeed
|
27
43
|
Then I should receive valid JSON
|
28
44
|
And each plugin instance should have a different color
|
29
|
-
|
45
|
+
|
30
46
|
Scenario Outline: Return only one colour per metric
|
31
|
-
|
47
|
+
Given a list of hosts exist
|
48
|
+
When I visit "<path>" on the first available host
|
32
49
|
Then the request should succeed
|
33
50
|
Then I should receive valid JSON
|
34
51
|
And each plugin instance should have a different color
|
35
52
|
|
36
|
-
Examples:
|
53
|
+
Examples:
|
37
54
|
| path |
|
38
55
|
| cpu-0/cpu-user |
|
39
56
|
| df/df-root |
|
40
57
|
|
41
58
|
Scenario: Retrieve single plugin instance with a color definition
|
42
|
-
|
59
|
+
Given a list of hosts exist
|
60
|
+
When I visit "swap/swap-used" on the first available host
|
43
61
|
Then the request should succeed
|
44
62
|
Then I should receive valid JSON
|
45
63
|
And the plugin instance should have a color
|
46
64
|
|
47
65
|
Scenario: Retrieve multiple plugins through a glob
|
48
|
-
Given
|
49
|
-
When I
|
66
|
+
Given a list of hosts exist
|
67
|
+
When I visit "disk*/disk_ops" on the first available host
|
50
68
|
Then the request should succeed
|
51
69
|
Then I should receive valid JSON
|
52
70
|
And I should see multiple plugins
|
53
71
|
|
54
|
-
Scenario
|
55
|
-
|
56
|
-
When I go to /data/<host>/libvirt/virt_cpu_total
|
72
|
+
Scenario: Retrieve multple hosts through a glob
|
73
|
+
When I go to /data/*/memory
|
57
74
|
Then the request should succeed
|
58
75
|
Then I should receive valid JSON
|
59
76
|
And I should see multiple hosts
|
60
77
|
|
61
|
-
Examples:
|
62
|
-
| host |
|
63
|
-
| * |
|
64
|
-
| %7Brgh,flapjack-test%7D |
|
65
|
-
|
66
|
-
|
data/features/site.feature
CHANGED
@@ -4,6 +4,8 @@ Feature: Visit site
|
|
4
4
|
Must be able to visualise the data
|
5
5
|
|
6
6
|
Scenario: Show available hosts
|
7
|
-
When I go to /
|
8
|
-
|
9
|
-
|
7
|
+
When I go to /profiles
|
8
|
+
And I visit the first profile
|
9
|
+
Then I should see a list of graphs
|
10
|
+
|
11
|
+
|
@@ -3,17 +3,26 @@ Then /^I should receive valid JSON$/ do
|
|
3
3
|
lambda {
|
4
4
|
@response = yajl.parse(response_body)
|
5
5
|
}.should_not raise_error
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
6
|
+
|
7
|
+
case
|
8
|
+
when @response.keys.first == "hosts"
|
9
|
+
@response["hosts"].should respond_to(:size)
|
10
|
+
when @response[@response.keys.first].respond_to?(:size)
|
11
|
+
host = @response.keys.first
|
12
|
+
plugins = @response[host]
|
13
|
+
plugins.size.should > 0
|
14
|
+
else
|
15
|
+
host = @response.keys.first
|
16
|
+
plugin = @response[host].keys.first
|
17
|
+
metric = @response[host][plugin].keys.first
|
18
|
+
|
19
|
+
host.should_not be_nil
|
20
|
+
plugin.should_not be_nil
|
21
|
+
metric.should_not be_nil
|
22
|
+
|
23
|
+
data = @response[host][plugin][metric]["data"]
|
24
|
+
end
|
25
|
+
|
17
26
|
end
|
18
27
|
|
19
28
|
Then /^I should receive JSON wrapped in a callback named "([^\"]*)"$/ do |callback|
|
@@ -77,3 +86,38 @@ Then /^I should see multiple hosts$/ do
|
|
77
86
|
@response.should_not be_nil
|
78
87
|
@response.keys.size.should > 1
|
79
88
|
end
|
89
|
+
|
90
|
+
Then /^the JSON should have a list of hosts$/ do
|
91
|
+
@response["hosts"].size.should > 0
|
92
|
+
end
|
93
|
+
|
94
|
+
Given /^a list of hosts exist$/ do
|
95
|
+
When 'I go to /data'
|
96
|
+
Then 'the request should succeed'
|
97
|
+
Then 'I should receive valid JSON'
|
98
|
+
Then 'the JSON should have a list of hosts'
|
99
|
+
end
|
100
|
+
|
101
|
+
When /^I visit "([^"]*)" on the first available host$/ do |glob|
|
102
|
+
host = @response["hosts"].first
|
103
|
+
url = "/data/#{host}/#{glob}"
|
104
|
+
When "I go to #{url}"
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
When /^I visit the first available host$/ do
|
109
|
+
When 'I go to /data'
|
110
|
+
Then 'the request should succeed'
|
111
|
+
Then 'I should receive valid JSON'
|
112
|
+
Then 'the JSON should have a list of hosts'
|
113
|
+
|
114
|
+
host = @response["hosts"].first
|
115
|
+
url = "/data/#{host}"
|
116
|
+
When "I go to #{url}"
|
117
|
+
end
|
118
|
+
|
119
|
+
Then /^the JSON should have a list of plugins$/ do
|
120
|
+
host = @response.keys.first
|
121
|
+
plugins = @response[host]
|
122
|
+
plugins.size.should > 0
|
123
|
+
end
|
@@ -1,4 +1,11 @@
|
|
1
|
-
|
1
|
+
When /^I visit the first profile$/ do
|
2
2
|
doc = Nokogiri::HTML(response_body)
|
3
|
-
doc.search('div#
|
3
|
+
link_text = doc.search('div#profiles ul a').first['href']
|
4
|
+
|
5
|
+
visit(link_text)
|
6
|
+
end
|
7
|
+
|
8
|
+
Then /^I should see a list of graphs$/ do
|
9
|
+
doc = Nokogiri::HTML(response_body)
|
10
|
+
doc.search('div#profile div.graph').size.should > 1
|
4
11
|
end
|
data/features/support/env.rb
CHANGED
@@ -23,10 +23,10 @@ class SinatraWorld
|
|
23
23
|
include Webrat::Methods
|
24
24
|
include Webrat::Matchers
|
25
25
|
|
26
|
-
Webrat::Methods.delegate_to_session :response_code, :response_body
|
26
|
+
Webrat::Methods.delegate_to_session :response_code, :response_body, :response_headers, :response
|
27
27
|
|
28
28
|
def app
|
29
|
-
|
29
|
+
Visage::JSON
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
data/lib/visage/collectd/json.rb
CHANGED
@@ -1,11 +1,10 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
|
4
|
-
$: <<
|
5
|
-
|
3
|
+
@root = Pathname.new(File.dirname(__FILE__)).parent.parent.parent.expand_path
|
4
|
+
$: << @root.to_s
|
5
|
+
require 'lib/visage/patches'
|
6
6
|
require 'errand'
|
7
7
|
require 'yajl'
|
8
|
-
require 'patches'
|
9
8
|
|
10
9
|
# Exposes RRDs as JSON.
|
11
10
|
#
|
@@ -0,0 +1,53 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$: << File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..'))
|
4
|
+
|
5
|
+
require 'lib/visage/patches'
|
6
|
+
|
7
|
+
module Visage
|
8
|
+
module Collectd
|
9
|
+
class RRDs
|
10
|
+
|
11
|
+
class << self
|
12
|
+
def rrddir
|
13
|
+
@rrddir = "/var/lib/collectd/rrd"
|
14
|
+
end
|
15
|
+
|
16
|
+
def hosts(opts={})
|
17
|
+
case
|
18
|
+
when opts[:hosts].blank?
|
19
|
+
glob = "*"
|
20
|
+
when opts[:hosts] =~ /,/
|
21
|
+
glob = "{#{opts[:hosts].strip.gsub(/\s*/, '').gsub(/,$/, '')}}"
|
22
|
+
else
|
23
|
+
glob = opts[:hosts]
|
24
|
+
end
|
25
|
+
|
26
|
+
Dir.glob("#{rrddir}/#{glob}").map {|e| e.split('/').last }.sort.uniq
|
27
|
+
end
|
28
|
+
|
29
|
+
def metrics(opts={})
|
30
|
+
case
|
31
|
+
when opts[:metrics].blank?
|
32
|
+
glob = "*/*"
|
33
|
+
when opts[:metrics] =~ /,/
|
34
|
+
puts "\n" * 4
|
35
|
+
glob = "{" + opts[:metrics].split(/\s*,\s*/).map { |m|
|
36
|
+
m =~ /\// ? m : ["*/#{m}", "#{m}/*"]
|
37
|
+
}.join(',').gsub(/,$/, '') + "}"
|
38
|
+
when opts[:metrics] !~ /\//
|
39
|
+
glob = "#{opts[:metrics]}/#{opts[:metrics]}"
|
40
|
+
else
|
41
|
+
glob = opts[:metrics]
|
42
|
+
end
|
43
|
+
|
44
|
+
host_glob = (opts[:host] || "*")
|
45
|
+
|
46
|
+
Dir.glob("#{rrddir}/#{host_glob}/#{glob}.rrd").map {|e| e.split('/')[-2..-1].join('/').gsub(/\.rrd$/, '')}.sort.uniq
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/visage/config/init.rb
CHANGED
@@ -1,30 +1,34 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
@root = Pathname.new(File.dirname(__FILE__)).parent.parent.parent.expand_path
|
4
|
+
@config_directory = Pathname.new(File.dirname(__FILE__)).expand_path
|
5
|
+
require @root.join('lib/visage/config')
|
5
6
|
require 'yaml'
|
6
7
|
|
7
8
|
Visage::Config.use do |c|
|
8
|
-
|
9
|
-
|
10
|
-
profile_filename = File.join(__DIR__, 'profiles.yaml')
|
9
|
+
# setup profiles file
|
10
|
+
profile_filename = @config_directory.join('profiles.yaml')
|
11
11
|
unless File.exists?(profile_filename)
|
12
|
-
|
13
|
-
puts "Check out config/profiles.yaml.sample for an example."
|
14
|
-
exit 1
|
15
|
-
end
|
16
|
-
YAML::load(File.read(profile_filename)).each_pair do |key, value|
|
17
|
-
c[key] = value
|
12
|
+
FileUtils.touch(profile_filename)
|
18
13
|
end
|
19
14
|
|
20
|
-
|
15
|
+
# setup plugin colors file
|
16
|
+
plugin_colors_filename = @config_directory.join('plugin-colors.yaml')
|
21
17
|
unless File.exists?(plugin_colors_filename)
|
22
18
|
puts "It's highly recommended you specify graph line colors in config/plugin-colors.yaml!"
|
23
19
|
end
|
24
|
-
|
25
|
-
|
20
|
+
|
21
|
+
# load config from profiles + plugin colors file
|
22
|
+
[profile_filename, plugin_colors_filename].each do |filename|
|
23
|
+
if File.exists?(filename)
|
24
|
+
config = YAML::load_file(filename) || {}
|
25
|
+
config.each_pair {|key, value| c[key] = value}
|
26
|
+
end
|
26
27
|
end
|
27
28
|
|
29
|
+
# load fallback colors
|
30
|
+
c['fallback_colors'] = YAML::load(File.read(@config_directory.join('fallback-colors.yaml')))
|
31
|
+
|
28
32
|
# Location of collectd's RRD - you may want to edit this!
|
29
33
|
c['rrddir'] = "/var/lib/collectd/rrd"
|
30
34
|
|
data/lib/visage/config.ru
CHANGED
data/lib/visage/graph.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'digest/md5'
|
4
|
+
|
5
|
+
module Visage
|
6
|
+
class Graph
|
7
|
+
|
8
|
+
attr_accessor :host, :plugin, :instances
|
9
|
+
|
10
|
+
def initialize(opts={})
|
11
|
+
@host = opts[:host]
|
12
|
+
@plugin = opts[:plugin]
|
13
|
+
@instances = opts[:instances]
|
14
|
+
end
|
15
|
+
|
16
|
+
def id
|
17
|
+
Digest::MD5.hexdigest("#{@host}-#{@plugin}-#{@instances}\n")
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
data/lib/visage/helpers.rb
CHANGED
@@ -0,0 +1,126 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
root = Pathname.new(File.dirname(__FILE__)).parent.parent
|
4
|
+
$: << root.join('lib')
|
5
|
+
require 'lib/visage/graph'
|
6
|
+
require 'lib/visage/patches'
|
7
|
+
require 'digest/md5'
|
8
|
+
|
9
|
+
module Visage
|
10
|
+
class Profile
|
11
|
+
attr_reader :options, :selected_hosts, :hosts, :selected_metrics, :metrics,
|
12
|
+
:name, :errors
|
13
|
+
|
14
|
+
@@root = Pathname.new(File.dirname(__FILE__)).parent.parent.expand_path
|
15
|
+
@@profiles_filename = @@root.join('lib/visage/config/profiles.yaml')
|
16
|
+
|
17
|
+
def self.get(id)
|
18
|
+
url = id.downcase.gsub(/[^\w]+/, "+")
|
19
|
+
profiles = YAML::load_file(@@profiles_filename) || {}
|
20
|
+
profiles[url] ? self.new(profiles[url]) : nil
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.all
|
24
|
+
profiles = YAML::load_file(@@profiles_filename) || {}
|
25
|
+
profiles.values.map { |prof| self.new(prof) }
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize(opts={})
|
29
|
+
@options = opts
|
30
|
+
@options[:url] = @options[:profile_name] ? @options[:profile_name].downcase.gsub(/[^\w]+/, "+") : nil
|
31
|
+
@errors = {}
|
32
|
+
|
33
|
+
# FIXME: this is nasty
|
34
|
+
# FIXME: doesn't work if there's only one host
|
35
|
+
# FIXME: add regex matching option
|
36
|
+
if @options[:hosts].blank?
|
37
|
+
@selected_hosts = []
|
38
|
+
@hosts = Visage::Collectd::RRDs.hosts
|
39
|
+
else
|
40
|
+
@selected_hosts = Visage::Collectd::RRDs.hosts(:hosts => @options[:hosts])
|
41
|
+
@hosts = Visage::Collectd::RRDs.hosts - @selected_hosts
|
42
|
+
end
|
43
|
+
|
44
|
+
if @options[:metrics].blank?
|
45
|
+
@selected_metrics = []
|
46
|
+
@metrics = Visage::Collectd::RRDs.metrics
|
47
|
+
else
|
48
|
+
@selected_metrics = Visage::Collectd::RRDs.metrics(:metrics => @options[:metrics])
|
49
|
+
@metrics = Visage::Collectd::RRDs.metrics - @selected_metrics
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Hashed based access to @options.
|
54
|
+
def method_missing(method)
|
55
|
+
@options[method]
|
56
|
+
end
|
57
|
+
|
58
|
+
def save
|
59
|
+
if valid?
|
60
|
+
# Construct record.
|
61
|
+
attrs = { :hosts => @options[:hosts],
|
62
|
+
:metrics => @options[:metrics],
|
63
|
+
:profile_name => @options[:profile_name],
|
64
|
+
:url => @options[:profile_name].downcase.gsub(/[^\w]+/, "+") }
|
65
|
+
|
66
|
+
# Save it.
|
67
|
+
profiles = YAML::load_file(@@profiles_filename) || {}
|
68
|
+
profiles[attrs[:url]] = attrs
|
69
|
+
|
70
|
+
File.open(@@profiles_filename, 'w') do |file|
|
71
|
+
file << profiles.to_yaml
|
72
|
+
end
|
73
|
+
|
74
|
+
true
|
75
|
+
else
|
76
|
+
false
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def valid?
|
81
|
+
valid_profile_name?
|
82
|
+
end
|
83
|
+
|
84
|
+
def graphs
|
85
|
+
graphs = []
|
86
|
+
|
87
|
+
hosts = Visage::Collectd::RRDs.hosts(:hosts => @options[:hosts])
|
88
|
+
metrics = @options[:metrics]
|
89
|
+
hosts.each do |host|
|
90
|
+
attrs = {}
|
91
|
+
globs = Visage::Collectd::RRDs.metrics(:host => host, :metrics => metrics)
|
92
|
+
globs.each do |n|
|
93
|
+
parts = n.split('/')
|
94
|
+
plugin = parts[0]
|
95
|
+
instance = parts[1]
|
96
|
+
attrs[plugin] ||= []
|
97
|
+
attrs[plugin] << instance
|
98
|
+
end
|
99
|
+
|
100
|
+
attrs.each_pair do |plugin, instances|
|
101
|
+
graphs << Visage::Graph.new(:host => host,
|
102
|
+
:plugin => plugin,
|
103
|
+
:instances => instances)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
graphs
|
108
|
+
end
|
109
|
+
|
110
|
+
def private_id
|
111
|
+
Digest::MD5.hexdigest("#{@options[:url]}\n")
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def valid_profile_name?
|
117
|
+
if @options[:profile_name].blank?
|
118
|
+
@errors[:profile_name] = "Profile name must not be blank."
|
119
|
+
false
|
120
|
+
else
|
121
|
+
true
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
end
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,7 @@
|
|
1
|
+
/*
|
2
|
+
* g.Raphael 0.4.1 - Charting library, based on Raphaël
|
3
|
+
*
|
4
|
+
* Copyright (c) 2009 Dmitry Baranovskiy (http://g.raphaeljs.com)
|
5
|
+
* Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
|
6
|
+
*/
|
7
|
+
Raphael.fn.g.linechart=function(J,I,a,c,s,r,C){function B(y,Y){var x=y.length/Y,V=0,i=x,X=0,W=[];while(V<y.length){i--;if(i<0){X+=y[V]*(1+i);W.push(X/x);X=y[V++]*-i;i+=x;}else{X+=y[V++];}}return W;}C=C||{};if(!this.raphael.is(s[0],"array")){s=[s];}if(!this.raphael.is(r[0],"array")){r=[r];}var O=Array.prototype.concat.apply([],s),M=Array.prototype.concat.apply([],r),o=this.g.snapEnds(Math.min.apply(Math,O),Math.max.apply(Math,O),s[0].length-1),v=o.from,h=o.to,l=C.gutter||10,P=(a-l*2)/(h-v),G=this.g.snapEnds(Math.min.apply(Math,M),Math.max.apply(Math,M),r[0].length-1),u=G.from,g=G.to,N=(c-l*2)/(g-u),t=Math.max(s[0].length,r[0].length),n=C.symbol||"",K=C.colors||Raphael.fn.g.colors,H=this,p=null,k=null,T=this.set(),L=[];for(var S=0,E=r.length;S<E;S++){t=Math.max(t,r[S].length);}var U=this.set();for(var S=0,E=r.length;S<E;S++){if(C.shade){U.push(this.path().attr({stroke:"none",fill:K[S],opacity:C.nostroke?1:0.3}));}if(r[S].length>a-2*l){r[S]=B(r[S],a-2*l);t=a-2*l;}if(s[S]&&s[S].length>a-2*l){s[S]=B(s[S],a-2*l);}}var w=this.set();if(C.axis){var f=(C.axis+"").split(/[,\s]+/);+f[0]&&w.push(this.g.axis(J+l,I+l,a-2*l,v,h,C.axisxstep||Math.floor((a-2*l)/20),2));+f[1]&&w.push(this.g.axis(J+a-l,I+c-l,c-2*l,u,g,C.axisystep||Math.floor((c-2*l)/20),3));+f[2]&&w.push(this.g.axis(J+l,I+c-l,a-2*l,v,h,C.axisxstep||Math.floor((a-2*l)/20),0));+f[3]&&w.push(this.g.axis(J+l,I+c-l,c-2*l,u,g,C.axisystep||Math.floor((c-2*l)/20),1));}var F=this.set(),Q=this.set(),m;for(var S=0,E=r.length;S<E;S++){if(!C.nostroke){F.push(m=this.path().attr({stroke:K[S],"stroke-width":C.width||2,"stroke-linejoin":"round","stroke-linecap":"round","stroke-dasharray":C.dash||""}));}var b=this.raphael.is(n,"array")?n[S]:n,z=this.set();L=[];for(var R=0,q=r[S].length;R<q;R++){var e=J+l+((s[S]||s[0])[R]-v)*P;var d=I+c-l-(r[S][R]-u)*N;(Raphael.is(b,"array")?b[R]:b)&&z.push(this.g[Raphael.fn.g.markers[this.raphael.is(b,"array")?b[R]:b]](e,d,(C.width||2)*3).attr({fill:K[S],stroke:"none"}));L=L.concat([R?"L":"M",e,d]);}Q.push(z);if(C.shade){U[S].attr({path:L.concat(["L",e,I+c-l,"L",J+l+((s[S]||s[0])[0]-v)*P,I+c-l,"z"]).join(",")});}!C.nostroke&&m.attr({path:L.join(",")});}function D(ae){var ab=[];for(var ac=0,ag=s.length;ac<ag;ac++){ab=ab.concat(s[ac]);}ab.sort();var ah=[],Y=[];for(var ac=0,ag=ab.length;ac<ag;ac++){ab[ac]!=ab[ac-1]&&ah.push(ab[ac])&&Y.push(J+l+(ab[ac]-v)*P);}ab=ah;ag=ab.length;var W=ae||H.set();for(var ac=0;ac<ag;ac++){var V=Y[ac]-(Y[ac]-(Y[ac-1]||J))/2,af=((Y[ac+1]||J+a)-Y[ac])/2+(Y[ac]-(Y[ac-1]||J))/2,x;ae?(x={}):W.push(x=H.rect(V-1,I,Math.max(af+1,1),c).attr({stroke:"none",fill:"#000",opacity:0}));x.values=[];x.symbols=H.set();x.y=[];x.x=Y[ac];x.axis=ab[ac];for(var aa=0,ad=r.length;aa<ad;aa++){ah=s[aa]||s[0];for(var Z=0,y=ah.length;Z<y;Z++){if(ah[Z]==ab[ac]){x.values.push(r[aa][Z]);x.y.push(I+c-l-(r[aa][Z]-u)*N);x.symbols.push(T.symbols[aa][Z]);}}}ae&&ae.call(x);}!ae&&(p=W);}function A(ac){var W=ac||H.set(),x;for(var aa=0,ae=r.length;aa<ae;aa++){for(var Z=0,ab=r[aa].length;Z<ab;Z++){var V=J+l+((s[aa]||s[0])[Z]-v)*P,ad=J+l+((s[aa]||s[0])[Z?Z-1:1]-v)*P,y=I+c-l-(r[aa][Z]-u)*N;ac?(x={}):W.push(x=H.circle(V,y,Math.abs(ad-V)/2).attr({stroke:"none",fill:"#000",opacity:0}));x.x=V;x.y=y;x.value=r[aa][Z];x.line=T.lines[aa];x.shade=T.shades[aa];x.symbol=T.symbols[aa][Z];x.symbols=T.symbols[aa];x.axis=(s[aa]||s[0])[Z];ac&&ac.call(x);}}!ac&&(k=W);}T.push(F,U,Q,w,p,k);T.lines=F;T.shades=U;T.symbols=Q;T.axis=w;T.hoverColumn=function(j,i){!p&&D();p.mouseover(j).mouseout(i);return this;};T.clickColumn=function(i){!p&&D();p.click(i);return this;};T.hrefColumn=function(W){var X=H.raphael.is(arguments[0],"array")?arguments[0]:arguments;if(!(arguments.length-1)&&typeof W=="object"){for(var j in W){for(var y=0,V=p.length;y<V;y++){if(p[y].axis==j){p[y].attr("href",W[j]);}}}}!p&&D();for(var y=0,V=X.length;y<V;y++){p[y]&&p[y].attr("href",X[y]);}return this;};T.hover=function(j,i){!k&&A();k.mouseover(j).mouseout(i);return this;};T.click=function(i){!k&&A();k.click(i);return this;};T.each=function(i){A(i);return this;};T.eachColumn=function(i){D(i);return this;};return T;};
|
@@ -1,5 +1,5 @@
|
|
1
|
-
|
2
|
-
* g.Raphael 0.4 - Charting library, based on Raphaël
|
1
|
+
/*!
|
2
|
+
* g.Raphael 0.4.1 - Charting library, based on Raphaël
|
3
3
|
*
|
4
4
|
* Copyright (c) 2009 Dmitry Baranovskiy (http://g.raphaeljs.com)
|
5
5
|
* Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
|
@@ -0,0 +1,7 @@
|
|
1
|
+
/*
|
2
|
+
* g.Raphael 0.4.1 - Charting library, based on Raphaël
|
3
|
+
*
|
4
|
+
* Copyright (c) 2009 Dmitry Baranovskiy (http://g.raphaeljs.com)
|
5
|
+
* Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
|
6
|
+
*/
|
7
|
+
(function(){var a=Math.max,c=Math.min;Raphael.fn.g=Raphael.fn.g||{};Raphael.fn.g.markers={disc:"disc",o:"disc",flower:"flower",f:"flower",diamond:"diamond",d:"diamond",square:"square",s:"square",triangle:"triangle",t:"triangle",star:"star","*":"star",cross:"cross",x:"cross",plus:"plus","+":"plus",arrow:"arrow","->":"arrow"};Raphael.fn.g.shim={stroke:"none",fill:"#000","fill-opacity":0};Raphael.fn.g.txtattr={font:"12px Arial, sans-serif"};Raphael.fn.g.colors=[];var e=[0.6,0.2,0.05,0.1333,0.75,0];for(var b=0;b<10;b++){if(b<e.length){Raphael.fn.g.colors.push("hsb("+e[b]+", .75, .75)");}else{Raphael.fn.g.colors.push("hsb("+e[b-e.length]+", 1, .5)");}}Raphael.fn.g.text=function(f,h,g){return this.text(f,h,g).attr(this.g.txtattr);};Raphael.fn.g.labelise=function(f,h,g){if(f){return(f+"").replace(/(##+(?:\.#+)?)|(%%+(?:\.%+)?)/g,function(i,k,j){if(k){return(+h).toFixed(k.replace(/^#+\.?/g,"").length);}if(j){return(h*100/g).toFixed(j.replace(/^%+\.?/g,"").length)+"%";}});}else{return(+h).toFixed(0);}};Raphael.fn.g.finger=function(l,k,g,m,h,i,j){if((h&&!m)||(!h&&!g)){return j?"":this.path();}i={square:"square",sharp:"sharp",soft:"soft"}[i]||"round";var o;m=Math.round(m);g=Math.round(g);l=Math.round(l);k=Math.round(k);switch(i){case"round":if(!h){var f=~~(m/2);if(g<f){f=g;o=["M",l+0.5,k+0.5-~~(m/2),"l",0,0,"a",f,~~(m/2),0,0,1,0,m,"l",0,0,"z"];}else{o=["M",l+0.5,k+0.5-f,"l",g-f,0,"a",f,f,0,1,1,0,m,"l",f-g,0,"z"];}}else{f=~~(g/2);if(m<f){f=m;o=["M",l-~~(g/2),k,"l",0,0,"a",~~(g/2),f,0,0,1,g,0,"l",0,0,"z"];}else{o=["M",l-f,k,"l",0,f-m,"a",f,f,0,1,1,g,0,"l",0,m-f,"z"];}}break;case"sharp":if(!h){var n=~~(m/2);o=["M",l,k+n,"l",0,-m,a(g-n,0),0,c(n,g),n,-c(n,g),n+(n*2<m),"z"];}else{n=~~(g/2);o=["M",l+n,k,"l",-g,0,0,-a(m-n,0),n,-c(n,m),n,c(n,m),n,"z"];}break;case"square":if(!h){o=["M",l,k+~~(m/2),"l",0,-m,g,0,0,m,"z"];}else{o=["M",l+~~(g/2),k,"l",1-g,0,0,-m,g-1,0,"z"];}break;case"soft":if(!h){f=c(g,Math.round(m/5));o=["M",l+0.5,k+0.5-~~(m/2),"l",g-f,0,"a",f,f,0,0,1,f,f,"l",0,m-f*2,"a",f,f,0,0,1,-f,f,"l",f-g,0,"z"];}else{f=c(Math.round(g/5),m);o=["M",l-~~(g/2),k,"l",0,f-m,"a",f,f,0,0,1,f,-f,"l",g-2*f,0,"a",f,f,0,0,1,f,f,"l",0,m-f,"z"];}}if(j){return o.join(",");}else{return this.path(o);}};Raphael.fn.g.disc=function(f,h,g){return this.circle(f,h,g);};Raphael.fn.g.line=function(f,h,g){return this.rect(f-g,h-g/5,2*g,2*g/5);};Raphael.fn.g.square=function(f,h,g){g=g*0.7;return this.rect(f-g,h-g,2*g,2*g);};Raphael.fn.g.triangle=function(f,h,g){g*=1.75;return this.path("M".concat(f,",",h,"m0-",g*0.58,"l",g*0.5,",",g*0.87,"-",g,",0z"));};Raphael.fn.g.diamond=function(f,h,g){return this.path(["M",f,h-g,"l",g,g,-g,g,-g,-g,g,-g,"z"]);};Raphael.fn.g.flower=function(j,h,f,g){f=f*1.25;var o=f,m=o*0.5;g=+g<3||!g?5:g;var p=["M",j,h+m,"Q"],l;for(var k=1;k<g*2+1;k++){l=k%2?o:m;p=p.concat([+(j+l*Math.sin(k*Math.PI/g)).toFixed(3),+(h+l*Math.cos(k*Math.PI/g)).toFixed(3)]);}p.push("z");return this.path(p.join(","));};Raphael.fn.g.star=function(f,n,m,h,g){h=h||m*0.382;g=g||5;var l=["M",f,n+h,"L"],k;for(var j=1;j<g*2;j++){k=j%2?m:h;l=l.concat([(f+k*Math.sin(j*Math.PI/g)),(n+k*Math.cos(j*Math.PI/g))]);}l.push("z");return this.path(l.join(","));};Raphael.fn.g.cross=function(f,h,g){g=g/2.5;return this.path("M".concat(f-g,",",h,"l",[-g,-g,g,-g,g,g,g,-g,g,g,-g,g,g,g,-g,g,-g,-g,-g,g,-g,-g,"z"]));};Raphael.fn.g.plus=function(f,h,g){g=g/2;return this.path("M".concat(f-g/2,",",h-g/2,"l",[0,-g,g,0,0,g,g,0,0,g,-g,0,0,g,-g,0,0,-g,-g,0,0,-g,"z"]));};Raphael.fn.g.arrow=function(f,h,g){return this.path("M".concat(f-g*0.7,",",h-g*0.4,"l",[g*0.6,0,0,-g*0.4,g,g*0.8,-g,g*0.8,0,-g*0.4,-g*0.6,0],"z"));};Raphael.fn.g.tag=function(f,m,l,k,i){k=k||0;i=i==null?5:i;l=l==null?"$9.99":l;var h=0.5522*i,g=this.set(),j=3;g.push(this.path().attr({fill:"#000",stroke:"#000"}));g.push(this.text(f,m,l).attr(this.g.txtattr).attr({fill:"#fff","font-family":"Helvetica, Arial"}));g.update=function(){this.rotate(0,f,m);var o=this[1].getBBox();if(o.height>=i*2){this[0].attr({path:["M",f,m+i,"a",i,i,0,1,1,0,-i*2,i,i,0,1,1,0,i*2,"m",0,-i*2-j,"a",i+j,i+j,0,1,0,0,(i+j)*2,"L",f+i+j,m+o.height/2+j,"l",o.width+2*j,0,0,-o.height-2*j,-o.width-2*j,0,"L",f,m-i-j].join(",")});}else{var n=Math.sqrt(Math.pow(i+j,2)-Math.pow(o.height/2+j,2));this[0].attr({path:["M",f,m+i,"c",-h,0,-i,h-i,-i,-i,0,-h,i-h,-i,i,-i,h,0,i,i-h,i,i,0,h,h-i,i,-i,i,"M",f+n,m-o.height/2-j,"a",i+j,i+j,0,1,0,0,o.height+2*j,"l",i+j-n+o.width+2*j,0,0,-o.height-2*j,"L",f+n,m-o.height/2-j].join(",")});}this[1].attr({x:f+i+j+o.width/2,y:m});k=(360-k)%360;this.rotate(k,f,m);k>90&&k<270&&this[1].attr({x:f-i-j-o.width/2,y:m,rotation:[180+k,f,m]});return this;};g.update();return g;};Raphael.fn.g.popupit=function(l,k,m,g,t){g=g==null?2:g;t=t||5;l=Math.round(l);k=Math.round(k);var j=m.getBBox(),n=Math.round(j.width/2),i=Math.round(j.height/2),s=[0,n+t*2,0,-n-t*2],o=[-i*2-t*3,-i-t,0,-i-t],f=["M",l-s[g],k-o[g],"l",-t,(g==2)*-t,-a(n-t,0),0,"a",t,t,0,0,1,-t,-t,"l",0,-a(i-t,0),(g==3)*-t,-t,(g==3)*t,-t,0,-a(i-t,0),"a",t,t,0,0,1,t,-t,"l",a(n-t,0),0,t,!g*-t,t,!g*t,a(n-t,0),0,"a",t,t,0,0,1,t,t,"l",0,a(i-t,0),(g==1)*t,t,(g==1)*-t,t,0,a(i-t,0),"a",t,t,0,0,1,-t,t,"l",-a(n-t,0),0,"z"].join(","),q=[{x:l,y:k+t*2+i},{x:l-t*2-n,y:k},{x:l,y:k-t*2-i},{x:l+t*2+n,y:k}][g];m.translate(q.x-n-j.x,q.y-i-j.y);return this.path(f).attr({fill:"#000",stroke:"none"}).insertBefore(m.node?m:m[0]);};Raphael.fn.g.popup=function(f,l,k,g,i){g=g==null?2:g>3?3:g;i=i||5;k=k||"$9.99";var h=this.set(),j=3;h.push(this.path().attr({fill:"#000",stroke:"#000"}));h.push(this.text(f,l,k).attr(this.g.txtattr).attr({fill:"#fff","font-family":"Helvetica, Arial"}));h.update=function(o,n,q){o=o||f;n=n||l;var t=this[1].getBBox(),u=t.width/2,s=t.height/2,y=[0,u+i*2,0,-u-i*2],v=[-s*2-i*3,-s-i,0,-s-i],m=["M",o-y[g],n-v[g],"l",-i,(g==2)*-i,-a(u-i,0),0,"a",i,i,0,0,1,-i,-i,"l",0,-a(s-i,0),(g==3)*-i,-i,(g==3)*i,-i,0,-a(s-i,0),"a",i,i,0,0,1,i,-i,"l",a(u-i,0),0,i,!g*-i,i,!g*i,a(u-i,0),0,"a",i,i,0,0,1,i,i,"l",0,a(s-i,0),(g==1)*i,i,(g==1)*-i,i,0,a(s-i,0),"a",i,i,0,0,1,-i,i,"l",-a(u-i,0),0,"z"].join(","),x=[{x:o,y:n+i*2+s},{x:o-i*2-u,y:n},{x:o,y:n-i*2-s},{x:o+i*2+u,y:n}][g];x.path=m;if(q){this.animate(x,500,">");}else{this.attr(x);}return this;};return h.update(f,l);};Raphael.fn.g.flag=function(f,k,j,i){i=i||0;j=j||"$9.99";var g=this.set(),h=3;g.push(this.path().attr({fill:"#000",stroke:"#000"}));g.push(this.text(f,k,j).attr(this.g.txtattr).attr({fill:"#fff","font-family":"Helvetica, Arial"}));g.update=function(l,o){this.rotate(0,l,o);var n=this[1].getBBox(),m=n.height/2;this[0].attr({path:["M",l,o,"l",m+h,-m-h,n.width+2*h,0,0,n.height+2*h,-n.width-2*h,0,"z"].join(",")});this[1].attr({x:l+m+h+n.width/2,y:o});i=360-i;this.rotate(i,l,o);i>90&&i<270&&this[1].attr({x:l-r-h-n.width/2,y:o,rotation:[180+i,l,o]});return this;};return g.update(f,k);};Raphael.fn.g.label=function(f,i,h){var g=this.set();g.push(this.rect(f,i,10,10).attr({stroke:"none",fill:"#000"}));g.push(this.text(f,i,h).attr(this.g.txtattr).attr({fill:"#fff"}));g.update=function(){var k=this[1].getBBox(),j=c(k.width+10,k.height+10)/2;this[0].attr({x:k.x-j/2,y:k.y-j/2,width:k.width+j,height:k.height+j,r:j});};g.update();return g;};Raphael.fn.g.labelit=function(h){var g=h.getBBox(),f=c(20,g.width+10,g.height+10)/2;return this.rect(g.x-f/2,g.y-f/2,g.width+f,g.height+f,f).attr({stroke:"none",fill:"#000"}).insertBefore(h.node?h:h[0]);};Raphael.fn.g.drop=function(f,k,j,h,i){h=h||30;i=i||0;var g=this.set();g.push(this.path(["M",f,k,"l",h,0,"A",h*0.4,h*0.4,0,1,0,f+h*0.7,k-h*0.7,"z"]).attr({fill:"#000",stroke:"none",rotation:[22.5-i,f,k]}));i=(i+90)*Math.PI/180;g.push(this.text(f+h*Math.sin(i),k+h*Math.cos(i),j).attr(this.g.txtattr).attr({"font-size":h*12/30,fill:"#fff"}));g.drop=g[0];g.text=g[1];return g;};Raphael.fn.g.blob=function(g,m,l,k,i){k=(+k+1?k:45)+90;i=i||12;var f=Math.PI/180,j=i*12/12;var h=this.set();h.push(this.path().attr({fill:"#000",stroke:"none"}));h.push(this.text(g+i*Math.sin((k)*f),m+i*Math.cos((k)*f)-j/2,l).attr(this.g.txtattr).attr({"font-size":j,fill:"#fff"}));h.update=function(t,s,y){t=t||g;s=s||m;var A=this[1].getBBox(),D=a(A.width+j,i*25/12),z=a(A.height+j,i*25/12),o=t+i*Math.sin((k-22.5)*f),B=s+i*Math.cos((k-22.5)*f),q=t+i*Math.sin((k+22.5)*f),C=s+i*Math.cos((k+22.5)*f),F=(q-o)/2,E=(C-B)/2,p=D/2,n=z/2,x=-Math.sqrt(Math.abs(p*p*n*n-p*p*E*E-n*n*F*F)/(p*p*E*E+n*n*F*F)),v=x*p*E/n+(q+o)/2,u=x*-n*F/p+(C+B)/2;if(y){this.animate({x:v,y:u,path:["M",g,m,"L",q,C,"A",p,n,0,1,1,o,B,"z"].join(",")},500,">");}else{this.attr({x:v,y:u,path:["M",g,m,"L",q,C,"A",p,n,0,1,1,o,B,"z"].join(",")});}return this;};h.update(g,m);return h;};Raphael.fn.g.colorValue=function(i,h,g,f){return"hsb("+[c((1-i/h)*0.4,1),g||0.75,f||0.75]+")";};Raphael.fn.g.snapEnds=function(n,o,m){var k=n,p=o;if(k==p){return{from:k,to:p,power:0};}function q(f){return Math.abs(f-0.5)<0.25?~~(f)+0.5:Math.round(f);}var l=(p-k)/m,g=~~(l),j=g,h=0;if(g){while(j){h--;j=~~(l*Math.pow(10,h))/Math.pow(10,h);}h++;}else{while(!g){h=h||1;g=~~(l*Math.pow(10,h))/Math.pow(10,h);h++;}h&&h--;}p=q(o*Math.pow(10,h))/Math.pow(10,h);if(p<o){p=q((o+0.5)*Math.pow(10,h))/Math.pow(10,h);}k=q((n-(h>0?0:0.5))*Math.pow(10,h))/Math.pow(10,h);return{from:k,to:p,power:h};};Raphael.fn.g.axis=function(v,u,o,G,l,J,m,L,n,g){g=g==null?2:g;n=n||"t";J=J||10;var F=n=="|"||n==" "?["M",v+0.5,u,"l",0,0.001]:m==1||m==3?["M",v+0.5,u,"l",0,-o]:["M",v,u+0.5,"l",o,0],z=this.g.snapEnds(G,l,J),K=z.from,B=z.to,I=z.power,H=0,C=this.set();d=(B-K)/J;var s=K,q=I>0?I:0;w=o/J;if(+m==1||+m==3){var h=u,A=(m-1?1:-1)*(g+3+!!(m-1));while(h>=u-o){n!="-"&&n!=" "&&(F=F.concat(["M",v-(n=="+"||n=="|"?g:!(m-1)*g*2),h+0.5,"l",g*2+1,0]));C.push(this.text(v+A,h,(L&&L[H++])||(Math.round(s)==s?s:+s.toFixed(q))).attr(this.g.txtattr).attr({"text-anchor":m-1?"start":"end"}));s+=d;h-=w;}if(Math.round(h+w-(u-o))){n!="-"&&n!=" "&&(F=F.concat(["M",v-(n=="+"||n=="|"?g:!(m-1)*g*2),u-o+0.5,"l",g*2+1,0]));C.push(this.text(v+A,u-o,(L&&L[H])||(Math.round(s)==s?s:+s.toFixed(q))).attr(this.g.txtattr).attr({"text-anchor":m-1?"start":"end"}));}}else{s=K;q=(I>0)*I;A=(m?-1:1)*(g+9+!m);var k=v,w=o/J,D=0,E=0;while(k<=v+o){n!="-"&&n!=" "&&(F=F.concat(["M",k+0.5,u-(n=="+"?g:!!m*g*2),"l",0,g*2+1]));C.push(D=this.text(k,u+A,(L&&L[H++])||(Math.round(s)==s?s:+s.toFixed(q))).attr(this.g.txtattr));var p=D.getBBox();if(E>=p.x-5){C.pop(C.length-1).remove();}else{E=p.x+p.width;}s+=d;k+=w;}if(Math.round(k-w-v-o)){n!="-"&&n!=" "&&(F=F.concat(["M",v+o+0.5,u-(n=="+"?g:!!m*g*2),"l",0,g*2+1]));C.push(this.text(v+o,u+A,(L&&L[H])||(Math.round(s)==s?s:+s.toFixed(q))).attr(this.g.txtattr));}}var M=this.path(F);M.text=C;M.all=this.set([M,C]);M.remove=function(){this.text.remove();this.constructor.prototype.remove.call(this);};return M;};Raphael.el.lighter=function(g){g=g||2;var f=[this.attrs.fill,this.attrs.stroke];this.fs=this.fs||[f[0],f[1]];f[0]=Raphael.rgb2hsb(Raphael.getRGB(f[0]).hex);f[1]=Raphael.rgb2hsb(Raphael.getRGB(f[1]).hex);f[0].b=c(f[0].b*g,1);f[0].s=f[0].s/g;f[1].b=c(f[1].b*g,1);f[1].s=f[1].s/g;this.attr({fill:"hsb("+[f[0].h,f[0].s,f[0].b]+")",stroke:"hsb("+[f[1].h,f[1].s,f[1].b]+")"});};Raphael.el.darker=function(g){g=g||2;var f=[this.attrs.fill,this.attrs.stroke];this.fs=this.fs||[f[0],f[1]];f[0]=Raphael.rgb2hsb(Raphael.getRGB(f[0]).hex);f[1]=Raphael.rgb2hsb(Raphael.getRGB(f[1]).hex);f[0].s=c(f[0].s*g,1);f[0].b=f[0].b/g;f[1].s=c(f[1].s*g,1);f[1].b=f[1].b/g;this.attr({fill:"hsb("+[f[0].h,f[0].s,f[0].b]+")",stroke:"hsb("+[f[1].h,f[1].s,f[1].b]+")"});};Raphael.el.original=function(){if(this.fs){this.attr({fill:this.fs[0],stroke:this.fs[1]});delete this.fs;}};})();
|