visage-app 0.1.8 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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;}};})();
|