mini-mini-profiler 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/Ruby/CHANGELOG +135 -0
  2. data/Ruby/README.md +161 -0
  3. data/Ruby/lib/html/flamegraph.html +325 -0
  4. data/Ruby/lib/html/includes.css +451 -0
  5. data/Ruby/lib/html/includes.js +945 -0
  6. data/Ruby/lib/html/includes.less +471 -0
  7. data/Ruby/lib/html/includes.tmpl +108 -0
  8. data/Ruby/lib/html/jquery.1.7.1.js +4 -0
  9. data/Ruby/lib/html/jquery.tmpl.js +486 -0
  10. data/Ruby/lib/html/list.css +9 -0
  11. data/Ruby/lib/html/list.js +38 -0
  12. data/Ruby/lib/html/list.tmpl +34 -0
  13. data/Ruby/lib/html/profile_handler.js +1 -0
  14. data/Ruby/lib/html/share.html +11 -0
  15. data/Ruby/lib/mini_profiler/client_settings.rb +65 -0
  16. data/Ruby/lib/mini_profiler/client_timer_struct.rb +78 -0
  17. data/Ruby/lib/mini_profiler/config.rb +57 -0
  18. data/Ruby/lib/mini_profiler/context.rb +11 -0
  19. data/Ruby/lib/mini_profiler/custom_timer_struct.rb +22 -0
  20. data/Ruby/lib/mini_profiler/flame_graph.rb +54 -0
  21. data/Ruby/lib/mini_profiler/gc_profiler.rb +107 -0
  22. data/Ruby/lib/mini_profiler/page_timer_struct.rb +58 -0
  23. data/Ruby/lib/mini_profiler/profiler.rb +544 -0
  24. data/Ruby/lib/mini_profiler/profiling_methods.rb +133 -0
  25. data/Ruby/lib/mini_profiler/request_timer_struct.rb +115 -0
  26. data/Ruby/lib/mini_profiler/sql_timer_struct.rb +58 -0
  27. data/Ruby/lib/mini_profiler/storage/abstract_store.rb +31 -0
  28. data/Ruby/lib/mini_profiler/storage/file_store.rb +111 -0
  29. data/Ruby/lib/mini_profiler/storage/memcache_store.rb +53 -0
  30. data/Ruby/lib/mini_profiler/storage/memory_store.rb +65 -0
  31. data/Ruby/lib/mini_profiler/storage/redis_store.rb +54 -0
  32. data/Ruby/lib/mini_profiler/timer_struct.rb +33 -0
  33. data/Ruby/lib/mini_profiler/version.rb +5 -0
  34. data/Ruby/lib/mini_profiler_rails/railtie.rb +107 -0
  35. data/Ruby/lib/patches/net_patches.rb +14 -0
  36. data/Ruby/lib/patches/sql_patches.rb +272 -0
  37. data/Ruby/lib/rack-mini-profiler.rb +7 -0
  38. data/mini-mini-profiler.gemspec +26 -0
  39. metadata +154 -0
data/Ruby/CHANGELOG ADDED
@@ -0,0 +1,135 @@
1
+ 28-June-2012 - Sam
2
+
3
+ * Started change log
4
+ * Corrected profiler so it properly captures POST requests (was supressing non 200s)
5
+ * Amended Rack.MiniProfiler.config[:user_provider] to use ip addres for identity
6
+ * Fixed bug where unviewed missing ids never got cleared
7
+ * Supress all '/assets/' in the rails tie (makes debugging easier)
8
+ * record_sql was mega buggy
9
+ * added MemcacheStore
10
+
11
+ 9-July-2012 - Sam
12
+
13
+ * Cleaned up mechanism for profiling in production, all you need to do now
14
+ is call Rack::MiniProfiler.authorize_request to get profiling working in
15
+ production
16
+ * Added option to display full backtraces pp=full-backtrace
17
+ * Cleaned up railties, got rid of the post authorize callback
18
+ * Version 0.1.3
19
+
20
+ 12-July-2012 - Sam
21
+
22
+ * Fixed incorrect profiling steps (was not indenting or measuring start time right
23
+ * Implemented native PG and MySql2 interceptors, this gives way more accurate times
24
+ * Refactored context so its a proper class and not a hash
25
+ * Added some more client probing built in to rails
26
+ * More tests
27
+
28
+ 18-July-2012 - Sam
29
+
30
+ * Added First Paint time for chrome
31
+ * Bug fix to ensure non Rails installs have mini profiler
32
+ * Version 0.1.7
33
+
34
+ 30-July-2012 - Sam
35
+
36
+ * Made compliant with ancient versions of Rack (including Rack used by Rails2)
37
+ * Fixed broken share link
38
+ * Fixed crashes on startup (in MemoryStore and FileStore)
39
+ * Version 0.1.8
40
+ * Unicode fix
41
+ * Version 0.1.9
42
+
43
+ 7-August-2012 - Sam
44
+
45
+ * Added option to disable profiler for the current session (pp=disable / pp=enable)
46
+ * yajl compatability contributed by Sven Riedel
47
+
48
+ 10-August-2012 - Sam
49
+
50
+ * Added basic prepared statement profiling for postgres
51
+
52
+ 20-August-2012 - Sam
53
+
54
+ * 1.12.pre
55
+ * Cap X-MiniProfiler-Ids at 10, otherwise the header can get killed
56
+
57
+ 3-September-2012 - Sam
58
+
59
+ * 1.13.pre
60
+ * pg gem prepared statements were not being logged correctly
61
+ * added setting config.backtrace_ignores = [] - an array of regexes that match on caller lines that get ignored
62
+ * added setting config.backtrace_includes = [] - an array of regexes that get included in the trace by default
63
+ * cleaned up the way client settings are stored
64
+ * made pp=full-backtrace "sticky"
65
+ * added pp=normal-backtrace to clear the "sticky" state
66
+ * change "pp=sample" to work with "caller" no need for stack trace gem
67
+
68
+ 4-September-2012 - Sam
69
+
70
+ * 1.15.pre
71
+ * fixed annoying bug where client settings were not sticking
72
+ * fixed long standing issue with Rack::ConditionalGet stopping MiniProfiler from working properly
73
+
74
+ 5-September-2012 - Sam
75
+
76
+ * 1.16
77
+ * fixed long standing problem specs (issue with memory store)
78
+ * fixed issue where profiler would be dumped when you got a 404 in production (and any time rails is bypassed)
79
+ * implemented stacktrace properly
80
+
81
+ 9-September-2012 - Sam
82
+
83
+ * 1.17
84
+ * pp=sample was bust unless stacktrace was installed
85
+
86
+ 10-September-2012 - Sam
87
+
88
+ * 1.19
89
+ * fix compat issue with 1.8.7
90
+
91
+ 12-September-2012 - Sam
92
+
93
+ * 1.20
94
+ * Added pp=profile-gc , it allows you to profile the GC in Ruby 1.9.3
95
+
96
+ 17-September-2012
97
+ * 1.21
98
+ * New MemchacedStore
99
+ * Rails 4 support
100
+
101
+ 17-September-2012
102
+ * Allow rack-mini-profiler to be sourced from github
103
+ * Extracted the pp=profile-gc-time out, the object space profiler needs to disable gc
104
+
105
+ 20-September-2012
106
+ * 1.22
107
+ * Fix permission issue in the gem
108
+
109
+ 8-April-2013
110
+ * 1.24
111
+ * Flame Graph Support see: http://samsaffron.com/archive/2013/03/19/flame-graphs-in-ruby-miniprofiler
112
+ * Fix file retention leak in file_store
113
+ * New toggle_shortcut and start_hidden options
114
+ * Fix for AngularJS support and MooTools
115
+ * More robust gc profiling
116
+ * Mongoid support
117
+ * Fix for html5 implicit body tags
118
+ * script tag initialized via data-attributes
119
+ * new - Rack::MiniProfiler.counter counter_name {}
120
+ * Allow usage of existing jQuery if its already loaded
121
+ * Fix pp=enable
122
+ * 1.8.7 support ... grrr
123
+ * Net:HTTP profiling
124
+ * pre authorize to run in all non development? and production? modes
125
+
126
+ 8-April-2013
127
+ * 1.25
128
+ * Missed flamegraph.html from build
129
+
130
+ 11-April-2013
131
+ * 1.26
132
+ * (minor) allow Rack::MiniProfilerRails.initialize!(Rails.application), for post config intialization
133
+
134
+
135
+
data/Ruby/README.md ADDED
@@ -0,0 +1,161 @@
1
+ # rack-mini-profiler
2
+
3
+ Middleware that displays speed badge for every html page. Designed to work both in production and in development.
4
+
5
+ ## Using rack-mini-profiler in your app
6
+
7
+ Install/add to Gemfile
8
+
9
+ ```ruby
10
+ gem 'rack-mini-profiler'
11
+ ```
12
+ Using Rails:
13
+
14
+ All you have to do is include the Gem and you're good to go in development.
15
+
16
+ rack-mini-profiler is designed with production profiling in mind. To enable that just run `Rack::MiniProfiler.authorize_request` once you know a request is allowed to profile.
17
+
18
+ Using Rails:
19
+
20
+ ```ruby
21
+ # A hook in your ApplicationController
22
+ def authorize
23
+ if current_user.is_admin?
24
+ Rack::MiniProfiler.authorize_request
25
+ end
26
+ end
27
+ ````
28
+
29
+
30
+ Using Builder:
31
+
32
+ ```ruby
33
+ require 'rack-mini-profiler'
34
+ builder = Rack::Builder.new do
35
+ use Rack::MiniProfiler
36
+
37
+ map('/') { run get }
38
+ end
39
+ ```
40
+
41
+ Using Sinatra:
42
+
43
+ ```ruby
44
+ require 'rack-mini-profiler'
45
+ class MyApp < Sinatra::Base
46
+ use Rack::MiniProfiler
47
+ end
48
+ ```
49
+
50
+ ## Database profiling
51
+
52
+ Currently supports Mysql2, Postgres, and Mongoid3 (with fallback support to ActiveRecord)
53
+
54
+ ## Storage
55
+
56
+ rack-mini-profiler stores it's results so they can be shared later and aren't lost at the end of the request.
57
+
58
+ There are 4 storage options: `MemoryStore`, `RedisStore`, `MemcacheStore`, and `FileStore`.
59
+
60
+ `FileStore` is the default in Rails environments and will write files to `tmp/miniprofiler/*`. `MemoryStore` is the default otherwise.
61
+
62
+ To change the default you can create a file in `config/initializers/mini_profiler.rb`
63
+
64
+ ```ruby
65
+ # set MemoryStore
66
+ Rack::MiniProfiler.config.storage = Rack::MiniProfiler::MemoryStore
67
+
68
+ # set RedisStore
69
+ if Rails.env.production?
70
+ uri = URI.parse(ENV["REDIS_SERVER_URL"])
71
+ Rack::MiniProfiler.config.storage_options = { :host => uri.host, :port => uri.port, :password => uri.password }
72
+ Rack::MiniProfiler.config.storage = Rack::MiniProfiler::RedisStore
73
+ end
74
+ ```
75
+
76
+ MemoryStore stores results in a processes heap - something that does not work well in a multi process environment.
77
+ FileStore stores results in the file system - something that may not work well in a multi machine environment.
78
+ RedisStore/MemcacheStore work in multi process and multi machine environments (RedisStore only saves results for up to 24 hours so it won't continue to fill up Redis).
79
+
80
+ Additionally you may implement an AbstractStore for your own provider.
81
+
82
+ ## User result segregation
83
+
84
+ MiniProfiler will attempt to keep all user results isolated, out-of-the-box the user provider uses the ip address:
85
+
86
+ ```ruby
87
+ Rack::MiniProfiler.config.user_provider = Proc.new{|env| Rack::Request.new(env).ip}
88
+ ```
89
+
90
+ You can override (something that is very important in a multi-machine production setup):
91
+
92
+ ```ruby
93
+ Rack::MiniProfiler.config.user_provider = Proc.new{ |env| CurrentUser.get(env) }
94
+ ```
95
+
96
+ The string this function returns should be unique for each user on the system (for anonymous you may need to fall back to ip address)
97
+
98
+ ## Running the Specs
99
+
100
+ ```
101
+ $ rake build
102
+ $ rake spec
103
+ ```
104
+
105
+ Additionally you can also run `autotest` if you like.
106
+
107
+ ## Configuration Options
108
+
109
+ You can set configuration options using the configuration accessor on Rack::MiniProfiler:
110
+
111
+ ```
112
+ # Have Mini Profiler show up on the right
113
+ Rack::MiniProfiler.config.position = 'right'
114
+ ```
115
+
116
+ In a Rails app, this can be done conveniently in an initializer such as config/initializers/mini_profiler.rb.
117
+
118
+ ## Rails 2.X support
119
+
120
+ To get MiniProfiler working with Rails 2.3.X you need to do the initialization manually as well as monkey patch away an incompatibility between activesupport and json_pure.
121
+
122
+ Add the following code to your environment.rb (or just in a specific environment such as development.rb) for initialization and configuration of MiniProfiler.
123
+
124
+ ```ruby
125
+ # configure and initialize MiniProfiler
126
+ require 'rack-mini-profiler'
127
+ c = ::Rack::MiniProfiler.config
128
+ c.pre_authorize_cb = lambda { |env|
129
+ Rails.env.development? || Rails.env.production?
130
+ }
131
+ tmp = Rails.root.to_s + "/tmp/miniprofiler"
132
+ FileUtils.mkdir_p(tmp) unless File.exists?(tmp)
133
+ c.storage_options = {:path => tmp}
134
+ c.storage = ::Rack::MiniProfiler::FileStore
135
+ config.middleware.use(::Rack::MiniProfiler)
136
+ ::Rack::MiniProfiler.profile_method(ActionController::Base, :process) {|action| "Executing action: #{action}"}
137
+ ::Rack::MiniProfiler.profile_method(ActionView::Template, :render) {|x,y| "Rendering: #{@virtual_path}"}
138
+
139
+ # monkey patch away an activesupport and json_pure incompatability
140
+ # http://pivotallabs.com/users/alex/blog/articles/1332-monkey-patch-of-the-day-activesupport-vs-json-pure-vs-ruby-1-8
141
+ if JSON.const_defined?(:Pure)
142
+ class JSON::Pure::Generator::State
143
+ include ActiveSupport::CoreExtensions::Hash::Except
144
+ end
145
+ end
146
+ ```
147
+
148
+ ## Available Options
149
+
150
+ * pre_authorize_cb - A lambda callback you can set to determine whether or not mini_profiler should be visible on a given request. Default in a Rails environment is only on in development mode. If in a Rack app, the default is always on.
151
+ * position - Can either be 'right' or 'left'. Default is 'left'.
152
+ * skip_schema_queries - Whether or not you want to log the queries about the schema of your tables. Default is 'false', 'true' in rails development.
153
+ * auto_inject (default true) - when false the miniprofiler script is not injected in the page
154
+ * backtrace_filter - a regex you can use to filter out unwanted lines from the backtraces
155
+ * toggle_shortcut (default Alt+P) - a jquery.hotkeys.js-style keyboard shortcut, used to toggle the mini_profiler's visibility. See http://code.google.com/p/js-hotkeys/ for more info.
156
+ * start_hidden (default false) - Whether or not you want the mini_profiler to be visible when loading a page
157
+
158
+ ## Special query strings
159
+
160
+ If you include the query string `pp=help` at the end of your request you will see the various options available. You can use these options to extend or contract the amount of diagnostics rack-mini-profiler gathers.
161
+
@@ -0,0 +1,325 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
5
+ <script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.0.8/d3.min.js"></script>
6
+ <!-- <script src="http://cdnjs.cloudflare.com/ajax/libs/sugar/1.3.8/sugar.min.js"></script> -->
7
+ <meta charset=utf-8 />
8
+ <title>Flame Graph of Page</title>
9
+ <style>
10
+ .info {height: 40px;}
11
+ .legend div {
12
+ display: block;
13
+ float: left;
14
+ width: 150px;
15
+ margin: 0 8px 8px;
16
+ padding: 4px;
17
+ height: 50px;
18
+ }
19
+ </style>
20
+ </head>
21
+ <body>
22
+ <div class="graph"></div>
23
+ <div class="info"></div>
24
+ <div class="legend"></div>
25
+
26
+ <script>
27
+
28
+ var data = /*DATA*/;
29
+ var maxX = 0;
30
+ var maxY = 0;
31
+
32
+ debounce = function(func, wait, trickle) {
33
+ var timeout;
34
+
35
+ timeout = null;
36
+ return function() {
37
+ var args, context, currentWait, later;
38
+ context = this;
39
+ args = arguments;
40
+ later = function() {
41
+ timeout = null;
42
+ return func.apply(context, args);
43
+ };
44
+
45
+ if (timeout && trickle) {
46
+ // already queued, let it through
47
+ return;
48
+ }
49
+
50
+ if (typeof wait === "function") {
51
+ currentWait = wait();
52
+ } else {
53
+ currentWait = wait;
54
+ }
55
+
56
+ if (timeout) {
57
+ clearTimeout(timeout);
58
+ }
59
+
60
+ timeout = setTimeout(later, currentWait);
61
+ return timeout;
62
+ };
63
+ };
64
+
65
+
66
+ var guessGem = function(frame)
67
+ {
68
+ var split = frame.split('/gems/');
69
+ if(split.length == 1) {
70
+ split = frame.split('/app/');
71
+ if(split.length == 1) {
72
+ split = frame.split('/lib/');
73
+ }
74
+
75
+ split = split[Math.max(split.length-2,0)].split('/');
76
+ return split[split.length-1];
77
+ }
78
+ else
79
+ {
80
+ return split[split.length -1].split('/')[0];
81
+ }
82
+ }
83
+
84
+ var guessMethod = function(frame) {
85
+ var split = frame.split('`');
86
+ if(split.length == 2) {
87
+ return split[1].split("'")[0];
88
+ }
89
+ return '?';
90
+ }
91
+
92
+ var guessFile = function(frame) {
93
+ var split = frame.split(".rb:");
94
+ if(split.length == 2) {
95
+ split = split[0].split('/');
96
+ return split[split.length - 1];
97
+ }
98
+ return "";
99
+ }
100
+
101
+ $.each(data, function(){
102
+ maxX = Math.max(maxX, this.x + this.width);
103
+ maxY = Math.max(maxY, this.y);
104
+ this.shortName = /* guessGem(this.frame) + " " + guessFile(this.frame) + " " */ guessMethod(this.frame);
105
+ });
106
+
107
+ var width = $(window).width();
108
+ var height = $(window).height() / 1.2;
109
+
110
+ $('.graph').width(width).height(height);
111
+
112
+ var xScale = d3.scale.linear()
113
+ .domain([0, maxX])
114
+ .range([0, width]);
115
+
116
+ var yScale = d3.scale.linear()
117
+ .domain([0, maxY])
118
+ .range([0,height]);
119
+
120
+ var realHeight = 0;
121
+ var debouncedHeightCheck = debounce(function(){
122
+ if (realHeight > 15) {
123
+ svg.selectAll('text').attr('display','show');
124
+ } else {
125
+ svg.selectAll('text').attr('display','none');
126
+ }
127
+ }, 200);
128
+
129
+ function zoom()
130
+ {
131
+ svg.attr("transform", "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
132
+
133
+ realHeight = yScale(1) * d3.event.scale;
134
+ debouncedHeightCheck();
135
+ }
136
+
137
+ var svg = d3.select(".graph")
138
+ .append("svg")
139
+ .attr("width", "100%")
140
+ .attr("height", "100%")
141
+ .attr("pointer-events", "all")
142
+ .append('svg:g')
143
+ .call(d3.behavior.zoom().on("zoom", zoom))
144
+ .append('svg:g');
145
+
146
+
147
+ // so zoom works everywhere
148
+ svg.append("rect")
149
+ .attr("x",function(d) { return xScale(0); })
150
+ .attr("y",function(d) { return yScale(0);})
151
+ .attr("width", function(d){return xScale(maxX);})
152
+ .attr("height", yScale(maxY))
153
+ .attr("fill", "white");
154
+
155
+ var color = function() {
156
+ var r = parseInt(205 + Math.random() * 50);
157
+ var g = parseInt(Math.random() * 230);
158
+ var b = parseInt(Math.random() * 55);
159
+ return "rgb(" + r + "," + g + "," + b + ")";
160
+ }
161
+ var info = {};
162
+
163
+ // http://stackoverflow.com/questions/1960473/unique-values-in-an-array
164
+ Array.prototype.getUnique = function() {
165
+ var o = {}, a = []
166
+ for (var i = 0; i < this.length; i++) o[this[i]] = 1
167
+ for (var e in o) a.push(e)
168
+ return a
169
+ }
170
+
171
+ var samplePercent = function(samples){
172
+ return "(" + samples +
173
+ " sample" + (samples == 1 ? "" : "s") + " - " +
174
+ ((samples / maxX) * 100).toFixed(2) + "%)";
175
+ }
176
+
177
+ var mouseover = function(d) {
178
+ var i = info[d.frame];
179
+ $('.info').text( d.frame + " " + samplePercent(i.samples.length));
180
+ d3.selectAll(i.nodes)
181
+ .attr('opacity',0.5);
182
+ };
183
+
184
+ var mouseout = function(d) {
185
+ var i = info[d.frame];
186
+ $('.info').text("");
187
+ d3.selectAll(i.nodes)
188
+ .attr('opacity',1);
189
+ };
190
+
191
+ // http://stackoverflow.com/a/7419630
192
+ var rainbow = function(numOfSteps, step) {
193
+ // This function generates vibrant, "evenly spaced" colours (i.e. no clustering). This is ideal for creating easily distiguishable vibrant markers in Google Maps and other apps.
194
+ // Adam Cole, 2011-Sept-14
195
+ // HSV to RBG adapted from: http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
196
+ var r, g, b;
197
+ var h = step / numOfSteps;
198
+ var i = ~~(h * 6);
199
+ var f = h * 6 - i;
200
+ var q = 1 - f;
201
+ switch(i % 6){
202
+ case 0: r = 1, g = f, b = 0; break;
203
+ case 1: r = q, g = 1, b = 0; break;
204
+ case 2: r = 0, g = 1, b = f; break;
205
+ case 3: r = 0, g = q, b = 1; break;
206
+ case 4: r = f, g = 0, b = 1; break;
207
+ case 5: r = 1, g = 0, b = q; break;
208
+ }
209
+ var c = "#" + ("00" + (~ ~(r * 255)).toString(16)).slice(-2) + ("00" + (~ ~(g * 255)).toString(16)).slice(-2) + ("00" + (~ ~(b * 255)).toString(16)).slice(-2);
210
+ return (c);
211
+ }
212
+
213
+ // assign some colors, analyze samples per gem
214
+ var gemStats = {}
215
+
216
+ $.each(data, function(){
217
+
218
+ var gem = guessGem(this.frame);
219
+ var stat = gemStats[gem];
220
+
221
+ if(!stat) {
222
+ gemStats[gem] = stat = {samples: [], frames: []};
223
+ }
224
+
225
+ stat.frames.push(this.frame);
226
+ for(var j=0; j < this.width; j++){
227
+ stat.samples.push(this.x + j);
228
+ }
229
+ });
230
+
231
+ var totalGems = 0;
232
+ $.each(gemStats, function(){totalGems++;});
233
+
234
+
235
+ var currentIndex = 0;
236
+ $.each(gemStats, function(k,stat){
237
+
238
+ stat.color = rainbow(totalGems, currentIndex);
239
+ stat.samples = stat.samples.getUnique();
240
+
241
+ for(var x=0; x < stat.frames.length; x++) {
242
+ info[stat.frames[x]] = {nodes: [], samples: [], color: stat.color};
243
+ }
244
+
245
+ currentIndex += 1;
246
+ });
247
+
248
+
249
+ // see: http://bl.ocks.org/mundhradevang/1387786
250
+ function fontSize(d,i) {
251
+ var size = yScale(1) / 3;
252
+ // var words = d.shortName.split(' ');
253
+ var word = d.shortName; // words[0];
254
+ var width = xScale(d.width+100);
255
+ var height = yScale(1);
256
+ var length = 0;
257
+ d3.select(this).style("font-size", size + "px").text(word);
258
+ while(((this.getBBox().width >= width) || (this.getBBox().height >= height)) && (size > 12))
259
+ {
260
+ size -= 0.1;
261
+ d3.select(this).style("font-size", size + "px");
262
+ }
263
+
264
+ d3.select(this).attr("dy", size);
265
+ }
266
+
267
+ svg.selectAll("g")
268
+ .data(data)
269
+ .enter()
270
+ .append("g")
271
+ .each(function(){
272
+ d3.select(this)
273
+ .append("rect")
274
+ .attr("x",function(d) { return xScale(d.x-1); })
275
+ .attr("y",function(d) { return yScale(maxY - d.y);})
276
+ .attr("width", function(d){return xScale(d.width);})
277
+ .attr("height", yScale(1))
278
+ .attr("fill", function(d){
279
+ var i = info[d.frame];
280
+ if(!i) {
281
+ info[d.frame] = i = {nodes: [], samples: [], color: color()};
282
+ }
283
+ i.nodes.push(this);
284
+ for(var j=0; j < d.width; j++){
285
+ i.samples.push(d.x + j);
286
+ }
287
+ return i.color;
288
+ })
289
+ .on("mouseover", mouseover)
290
+ .on("mouseout", mouseout);
291
+
292
+ d3.select(this)
293
+ .append("text")
294
+ .attr("x",function(d) { return xScale(d.x - 0.98); })
295
+ .attr("y",function(d) { return yScale(maxY - d.y);})
296
+ .on("mouseover", mouseover)
297
+ .on("mouseout", mouseout)
298
+ .each(fontSize)
299
+ .attr("display", "none");
300
+
301
+ });
302
+
303
+
304
+ // Samples may overlap on the same line
305
+ for (var r in info) {
306
+ if (info[r].samples) {
307
+ info[r].samples = info[r].samples.getUnique();
308
+ }
309
+ };
310
+
311
+
312
+ // render the legend
313
+ $.each(gemStats, function(k,v){
314
+ var node = $("<div></div>")
315
+ .css("background-color", v.color)
316
+ .text(k + " " + samplePercent(v.samples.length)) ;
317
+ $('.legend').append(node);
318
+ });
319
+
320
+
321
+
322
+ </script>
323
+ </body>
324
+ </html>
325
+