mini-mini-profiler 0.1

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.
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
+