rack-mini-profiler 2.0.0 → 2.1.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +24 -0
- data/README.md +59 -28
- data/lib/enable_rails_patches.rb +1 -3
- data/lib/html/includes.css +40 -9
- data/lib/html/includes.js +90 -29
- data/lib/html/includes.scss +32 -4
- data/lib/html/includes.tmpl +74 -2
- data/lib/html/profile_handler.js +1 -1
- data/lib/html/rack-mini-profiler.css +3 -0
- data/lib/html/rack-mini-profiler.js +2 -0
- data/lib/html/vendor.js +10 -2
- data/lib/mini_profiler/asset_version.rb +1 -1
- data/lib/mini_profiler/config.rb +13 -1
- data/lib/mini_profiler/profiler.rb +166 -23
- data/lib/mini_profiler/storage/abstract_store.rb +72 -0
- data/lib/mini_profiler/storage/memory_store.rb +54 -5
- data/lib/mini_profiler/storage/redis_store.rb +136 -2
- data/lib/mini_profiler/timer_struct/page.rb +4 -1
- data/lib/mini_profiler/version.rb +1 -1
- data/lib/mini_profiler_rails/railtie.rb +13 -0
- data/lib/mini_profiler_rails/railtie_methods.rb +6 -0
- data/lib/patches/net_patches.rb +19 -6
- data/lib/prepend_net_http_patch.rb +5 -0
- data/rack-mini-profiler.gemspec +4 -2
- metadata +51 -20
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 1603a0c0246f9b132f81e74331425a1217156d62f71f20a06ecd3edca91aef51
         | 
| 4 | 
            +
              data.tar.gz: a341e35cf639c11cc61c9ef5fd1ecd25e89ff12da299d3a6bfe8cb479c5dd7d8
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 4244e2738847e0e34b33cebda96f45d79ad82201ce969310bf45f1ad7431561af0d41610c2e8c60a0c64d428032a1e4bea6cc5b02aa71b5462a918474228844a
         | 
| 7 | 
            +
              data.tar.gz: b1c0d88134e780bbbb91f711366400585d51d98107d1f35af85ca7c38498f0e23de019f2eb05259e833d8deabf485dc70f1dd581c28e132aba68bfa785967774
         | 
    
        data/CHANGELOG.md
    CHANGED
    
    | @@ -1,5 +1,29 @@ | |
| 1 1 | 
             
            # CHANGELOG
         | 
| 2 2 |  | 
| 3 | 
            +
            ## 2.1.0 - 2020-09-17
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            - [FEATURE] Allow assets to be precompiled with Sprockets
         | 
| 6 | 
            +
            - [FEATURE] Snapshots sampling (see README in repo)
         | 
| 7 | 
            +
            - [FEATURE] Allow `skip_paths` config to contain regular expressions
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            ## 2.0.4 - 2020-08-04
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            - [FIX] webpacker may exist with no config, allow for that
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            ## 2.0.3 - 2020-07-29
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            - [FIX] support for deprecation free Redis 4.2
         | 
| 16 | 
            +
            - [FEATURE] skip /packs when serving static assets
         | 
| 17 | 
            +
            - [FEATURE] allow Net::HTTP patch to be applied with either prerpend or alias
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            ## 2.0.2 - 2020-05-25
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            - [FIX] client timings were not showing up when you clicked show trivial
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            ## 2.0.1 - 2020-03-17
         | 
| 24 | 
            +
             | 
| 25 | 
            +
            - [REVERT] Prepend Net::HTTP patch instead of class_eval and aliasing (#429) (technique clashes with New Relic and Skylight agents)
         | 
| 26 | 
            +
             | 
| 3 27 | 
             
            ## 2.0.0 - 2020-03-11
         | 
| 4 28 |  | 
| 5 29 | 
             
            - [FEATURE] Prepend Net::HTTP patch instead of class_eval and aliasing (#429)
         | 
    
        data/README.md
    CHANGED
    
    | @@ -29,7 +29,7 @@ If you feel like taking on any of this start an issue and update us on your prog | |
| 29 29 |  | 
| 30 30 | 
             
            ## Installation
         | 
| 31 31 |  | 
| 32 | 
            -
            Install/add to Gemfile in Ruby 2. | 
| 32 | 
            +
            Install/add to Gemfile in Ruby 2.4+
         | 
| 33 33 |  | 
| 34 34 | 
             
            ```ruby
         | 
| 35 35 | 
             
            gem 'rack-mini-profiler'
         | 
| @@ -65,6 +65,22 @@ If you don't want to manually require Mini Profiler: | |
| 65 65 | 
             
            gem 'rack-mini-profiler', require: ['enable_rails_patches', 'rack-mini-profiler']
         | 
| 66 66 | 
             
            ```
         | 
| 67 67 |  | 
| 68 | 
            +
            #### `Net::HTTP` stack level too deep errors
         | 
| 69 | 
            +
             | 
| 70 | 
            +
            If you start seeing `SystemStackError: stack level too deep` errors from `Net::HTTP` after installing Mini Profiler, this means there is another patch for `Net::HTTP#request` that conflicts with Mini Profiler's patch in your application. To fix this, change `rack-mini-profiler` gem line in your `Gemfile` to the following:
         | 
| 71 | 
            +
             | 
| 72 | 
            +
            ```ruby
         | 
| 73 | 
            +
            gem 'rack-mini-profiler', require: ['prepend_net_http_patch', 'rack-mini-profiler']
         | 
| 74 | 
            +
            ```
         | 
| 75 | 
            +
             | 
| 76 | 
            +
            If you currently have `require: false`, remove the `'rack-mini-profiler'` string from the `require` array above so the gem line becomes like this:
         | 
| 77 | 
            +
             | 
| 78 | 
            +
            ```ruby
         | 
| 79 | 
            +
            gem 'rack-mini-profiler', require: ['prepend_net_http_patch']
         | 
| 80 | 
            +
            ```
         | 
| 81 | 
            +
             | 
| 82 | 
            +
            This conflict happens when a ruby method is patched twice, once using module prepend, and once using method aliasing. See this [ruby issue](https://bugs.ruby-lang.org/issues/11120) for details. The fix is to apply all patches the same way. Mini Profiler by default will apply its patch using method aliasing, but you can change that to module prepend by adding `require: ['prepend_net_http_patch']` to the gem line as shown above.
         | 
| 83 | 
            +
             | 
| 68 84 | 
             
            #### Rails and manual initialization
         | 
| 69 85 |  | 
| 70 86 | 
             
            In case you need to make sure rack_mini_profiler is initialized after all other gems, or you want to execute some code before rack_mini_profiler required:
         | 
| @@ -170,6 +186,18 @@ There are two additional `pp` options that can be used to analyze memory which d | |
| 170 186 | 
             
            * Use `?pp=profile-gc` to report on Garbage Collection statistics
         | 
| 171 187 | 
             
            * Use `?pp=analyze-memory` to report on ObjectSpace statistics
         | 
| 172 188 |  | 
| 189 | 
            +
            ### Snapshots Sampling
         | 
| 190 | 
            +
             | 
| 191 | 
            +
            In a complex web application, it's possible for a request to trigger rare conditions that result in poor performance. Mini Profiler ships with a feature to help detect those rare conditions and fix them. It works by enabling invisible profiling on one request every N requests, and saving the performance metrics that are collected during the request (a.k.a snapshot of the request) so that they can be viewed later. To turn this feature on, set the `snapshot_every_n_requests` config to a value larger than 0. The larger the value is, the less frequently requests are profiled.
         | 
| 192 | 
            +
             | 
| 193 | 
            +
            Mini Profiler will exclude requests that are made to skippd paths (see `skip_paths` config below) from being sampled. Additionally, if profiling is enabled for a request that later finishes with a non-2xx status code, Mini Profiler will discard the snapshot and not save it (this behavior may change in the future).
         | 
| 194 | 
            +
             | 
| 195 | 
            +
            After enabling snapshots sampling, you can see the snapshots that have been collected at `/mini-profiler-resources/snapshots` (or if you changed the `base_url_path` config, substitute `mini-profiler-resources` with your value of the config). You'll see on that page a table where each row represents a group of snapshots with the duration of the worst snapshot in that group. The worst snapshot in a group is defined as the snapshot whose request took longer than all of the snapshots in the same group. Snapshots grouped by HTTP method and path of the request, and if your application is a Rails app, Mini Profiler will try to convert the path to `controller#action` and group by that instead of request path. Clicking on a group will display the snapshots of that group sorted from worst to best. From there, you can click on a snapshot's ID to see the snapshot with all the performance metrics that were collected.
         | 
| 196 | 
            +
             | 
| 197 | 
            +
            Access to the snapshots page is restricted to only those who can see the speed badge on their own requests, see the section below this one about access control.
         | 
| 198 | 
            +
             | 
| 199 | 
            +
            Mini Profiler will keep a maximum of 1000 snapshots by default, and you can change that via the `snapshots_limit` config. When snapshots reach the configured limit, Mini Profiler will save a new snapshot only if it's worse than at least one of the existing snapshots and delete the best one (i.e. the snapshot whose request took the least time compared to other snapshots).
         | 
| 200 | 
            +
             | 
| 173 201 | 
             
            ## Access control in non-development environments
         | 
| 174 202 |  | 
| 175 203 | 
             
            rack-mini-profiler is designed with production profiling in mind. To enable that run `Rack::MiniProfiler.authorize_request` once you know a request is allowed to profile.
         | 
| @@ -313,6 +341,15 @@ _Note:_ The GUID (`data-version` and the `?v=` parameter on the `src`) will chan | |
| 313 341 | 
             
            #### Using MiniProfiler's built in route for apps without HTML responses
         | 
| 314 342 | 
             
            MiniProfiler also ships with a `/rack-mini-profiler/requests` route that displays the speed badge on a blank HTML page. This can be useful when profiling an application that does not render HTML.
         | 
| 315 343 |  | 
| 344 | 
            +
            #### Register MiniProfiler's assets in the Rails assets pipeline
         | 
| 345 | 
            +
            MiniProfiler can be configured so it registers its assets in the assets pipeline. To do that, you'll need to provide a lambda (or proc) to the `assets_url` config (see the below section). The callback will receive 3 arguments which are: `name` represents asset name (currently it's either `rack-mini-profiling.js` or `rack-mini-profiling.css`), `assets_version` is a 32 characters long hash of MiniProfiler's assets, and `env` which is the `env` object of the request. MiniProfiler expects the `assets_url` callback to return a URL from which the asset can be loaded (the return value will be used as a `href`/`src` attribute in the DOM). If the `assets_url` callback is not set (the default) or it returns a non-truthy value, MiniProfiler will fallback to loading assets from its own middleware (`/mini-profiler-resources/*`). The following callback should work for most applications:
         | 
| 346 | 
            +
             | 
| 347 | 
            +
            ```ruby
         | 
| 348 | 
            +
            Rack::MiniProfiler.config.assets_url = ->(name, version, env) {
         | 
| 349 | 
            +
              ActionController::Base.helpers.asset_path(name)
         | 
| 350 | 
            +
            }
         | 
| 351 | 
            +
            ```
         | 
| 352 | 
            +
             | 
| 316 353 | 
             
            ### Configuration Options
         | 
| 317 354 |  | 
| 318 355 | 
             
            You can set configuration options using the configuration accessor on `Rack::MiniProfiler`.
         | 
| @@ -328,8 +365,8 @@ Option|Default|Description | |
| 328 365 | 
             
            -------|---|--------
         | 
| 329 366 | 
             
            pre_authorize_cb|Rails: dev only<br>Rack: always on|A lambda callback that returns true to make mini_profiler visible on a given request.
         | 
| 330 367 | 
             
            position|`'top-left'`|Display mini_profiler on `'top-right'`, `'top-left'`, `'bottom-right'` or `'bottom-left'`.
         | 
| 331 | 
            -
            skip_paths|`[]`| | 
| 332 | 
            -
            skip_schema_queries|Rails dev: ` | 
| 368 | 
            +
            skip_paths|`[]`|An array of paths that skip profiling. Both `String` and `Regexp` are acceptable in the array.
         | 
| 369 | 
            +
            skip_schema_queries|Rails dev: `true`<br>Othwerwise: `false`|`true` to skip schema queries.
         | 
| 333 370 | 
             
            auto_inject|`true`|`true` to inject the miniprofiler script in the page.
         | 
| 334 371 | 
             
            backtrace_ignores|`[]`|Regexes of lines to be removed from backtraces.
         | 
| 335 372 | 
             
            backtrace_includes|Rails: `[/^\/?(app\|config\|lib\|test)/]`<br>Rack: `[]`|Regexes of lines to keep in backtraces.
         | 
| @@ -344,33 +381,17 @@ max_traces_to_show|20|Maximum number of mini profiler timing blocks to show on o | |
| 344 381 | 
             
            html_container|`body`|The HTML container (as a jQuery selector) to inject the mini_profiler UI into
         | 
| 345 382 | 
             
            show_total_sql_count|`false`|Displays the total number of SQL executions.
         | 
| 346 383 | 
             
            enable_advanced_debugging_tools|`false`|Enables sensitive debugging tools that can be used via the UI. In production we recommend keeping this disabled as memory and environment debugging tools can expose contents of memory that may contain passwords.
         | 
| 384 | 
            +
            assets_url|`nil`|See the "Register MiniProfiler's assets in the Rails assets pipeline" section above.
         | 
| 385 | 
            +
            snapshot_every_n_requests|`-1`|Determines how frequently snapshots are taken. See the "Snapshots Sampling" above for more details.
         | 
| 386 | 
            +
            snapshots_limit|`1000`|Determines how many snapshots Mini Profiler is allowed to keep.
         | 
| 347 387 |  | 
| 348 | 
            -
            ###  | 
| 349 | 
            -
             | 
| 350 | 
            -
            If you are using `Rack::Deflate` with rails and rack-mini-profiler in its default configuration,
         | 
| 351 | 
            -
            `Rack::MiniProfiler` will be injected (as always) at position 0 in the middleware stack. This
         | 
| 352 | 
            -
            will result in it attempting to inject html into the already-compressed response body. To fix this,
         | 
| 353 | 
            -
            the middleware ordering must be overriden.
         | 
| 354 | 
            -
             | 
| 355 | 
            -
            To do this, first add `, require: false` to the gemfile entry for rack-mini-profiler.
         | 
| 356 | 
            -
            This will prevent the railtie from running. Then, customize the initialization
         | 
| 357 | 
            -
            in the initializer like so:
         | 
| 358 | 
            -
             | 
| 359 | 
            -
            ```ruby
         | 
| 360 | 
            -
            require 'rack-mini-profiler'
         | 
| 361 | 
            -
             | 
| 362 | 
            -
            Rack::MiniProfilerRails.initialize!(Rails.application)
         | 
| 363 | 
            -
             | 
| 364 | 
            -
            Rails.application.middleware.delete(Rack::MiniProfiler)
         | 
| 365 | 
            -
            Rails.application.middleware.insert_after(Rack::Deflater, Rack::MiniProfiler)
         | 
| 366 | 
            -
            ```
         | 
| 367 | 
            -
             | 
| 368 | 
            -
            Deleting the middleware and then reinserting it is a bit inelegant, but
         | 
| 369 | 
            -
            a sufficient and costless solution. It is possible that rack-mini-profiler might
         | 
| 370 | 
            -
            support this scenario more directly if it is found that
         | 
| 371 | 
            -
            there is significant need for this confriguration or that
         | 
| 372 | 
            -
            the above recipe causes problems.
         | 
| 388 | 
            +
            ### Using MiniProfiler with `Rack::Deflate` middleware
         | 
| 373 389 |  | 
| 390 | 
            +
            If you are using `Rack::Deflate` with Rails and `rack-mini-profiler` in its default configuration,
         | 
| 391 | 
            +
            `Rack::MiniProfiler` will be injected (as always) at position 0 in the middleware stack,
         | 
| 392 | 
            +
            which means it will run after `Rack::Deflate` on response processing. To prevent attempting to inject
         | 
| 393 | 
            +
            HTML in already compressed response body MiniProfiler will suppress compression by setting 
         | 
| 394 | 
            +
            `identity` encoding in `Accept-Encoding` request header.
         | 
| 374 395 |  | 
| 375 396 | 
             
            ## Special query strings
         | 
| 376 397 |  | 
| @@ -407,6 +428,16 @@ if JSON.const_defined?(:Pure) | |
| 407 428 | 
             
            end
         | 
| 408 429 | 
             
            ```
         | 
| 409 430 |  | 
| 431 | 
            +
            ## Development
         | 
| 432 | 
            +
             | 
| 433 | 
            +
            If you want to contribute to this project, that's great, thank you! You can run the following rake task:
         | 
| 434 | 
            +
             | 
| 435 | 
            +
            ```
         | 
| 436 | 
            +
            $ bundle exec rake client_dev
         | 
| 437 | 
            +
            ```
         | 
| 438 | 
            +
             | 
| 439 | 
            +
            which will start a local Sinatra server at `http://localhost:9292` where you'll be able to preview your changes. Refreshing the page should be enough to see any changes you make to files in the `lib/html` directory.
         | 
| 440 | 
            +
             | 
| 410 441 | 
             
            ## Running the Specs
         | 
| 411 442 |  | 
| 412 443 | 
             
            ```
         | 
    
        data/lib/enable_rails_patches.rb
    CHANGED
    
    
    
        data/lib/html/includes.css
    CHANGED
    
    | @@ -1,9 +1,20 @@ | |
| 1 1 | 
             
            @charset "UTF-8";
         | 
| 2 | 
            +
            .mp-snapshots,
         | 
| 2 3 | 
             
            .profiler-result,
         | 
| 3 4 | 
             
            .profiler-queries {
         | 
| 4 5 | 
             
              color: #555;
         | 
| 5 6 | 
             
              line-height: 1;
         | 
| 6 7 | 
             
              font-size: 12px; }
         | 
| 8 | 
            +
              .mp-snapshots pre,
         | 
| 9 | 
            +
              .mp-snapshots code,
         | 
| 10 | 
            +
              .mp-snapshots label,
         | 
| 11 | 
            +
              .mp-snapshots table,
         | 
| 12 | 
            +
              .mp-snapshots tbody,
         | 
| 13 | 
            +
              .mp-snapshots thead,
         | 
| 14 | 
            +
              .mp-snapshots tfoot,
         | 
| 15 | 
            +
              .mp-snapshots tr,
         | 
| 16 | 
            +
              .mp-snapshots th,
         | 
| 17 | 
            +
              .mp-snapshots td,
         | 
| 7 18 | 
             
              .profiler-result pre,
         | 
| 8 19 | 
             
              .profiler-result code,
         | 
| 9 20 | 
             
              .profiler-result label,
         | 
| @@ -33,27 +44,40 @@ | |
| 33 44 | 
             
                background-color: transparent;
         | 
| 34 45 | 
             
                overflow: visible;
         | 
| 35 46 | 
             
                max-height: none; }
         | 
| 47 | 
            +
              .mp-snapshots table,
         | 
| 36 48 | 
             
              .profiler-result table,
         | 
| 37 49 | 
             
              .profiler-queries table {
         | 
| 38 50 | 
             
                border-collapse: collapse;
         | 
| 39 51 | 
             
                border-spacing: 0; }
         | 
| 52 | 
            +
              .mp-snapshots a,
         | 
| 53 | 
            +
              .mp-snapshots a:hover,
         | 
| 40 54 | 
             
              .profiler-result a,
         | 
| 41 55 | 
             
              .profiler-result a:hover,
         | 
| 42 56 | 
             
              .profiler-queries a,
         | 
| 43 57 | 
             
              .profiler-queries a:hover {
         | 
| 44 58 | 
             
                cursor: pointer;
         | 
| 45 59 | 
             
                color: #0077cc; }
         | 
| 60 | 
            +
              .mp-snapshots a,
         | 
| 46 61 | 
             
              .profiler-result a,
         | 
| 47 62 | 
             
              .profiler-queries a {
         | 
| 48 63 | 
             
                text-decoration: none; }
         | 
| 64 | 
            +
                .mp-snapshots a:hover,
         | 
| 49 65 | 
             
                .profiler-result a:hover,
         | 
| 50 66 | 
             
                .profiler-queries a:hover {
         | 
| 51 67 | 
             
                  text-decoration: underline; }
         | 
| 68 | 
            +
              .mp-snapshots .custom-fields-title,
         | 
| 69 | 
            +
              .profiler-result .custom-fields-title,
         | 
| 70 | 
            +
              .profiler-queries .custom-fields-title {
         | 
| 71 | 
            +
                color: #555;
         | 
| 72 | 
            +
                font: Helvetica, Arial, sans-serif;
         | 
| 73 | 
            +
                font-size: 14px; }
         | 
| 52 74 |  | 
| 53 75 | 
             
            .profiler-result {
         | 
| 54 76 | 
             
              font-family: Helvetica, Arial, sans-serif; }
         | 
| 55 77 | 
             
              .profiler-result .profiler-toggle-duration-with-children {
         | 
| 56 78 | 
             
                float: right; }
         | 
| 79 | 
            +
              .profiler-result .profiler-snapshots-page-link {
         | 
| 80 | 
            +
                float: left; }
         | 
| 57 81 | 
             
              .profiler-result table.profiler-client-timings {
         | 
| 58 82 | 
             
                margin-top: 10px; }
         | 
| 59 83 | 
             
              .profiler-result .profiler-label {
         | 
| @@ -207,8 +231,7 @@ | |
| 207 231 | 
             
                top: 0px; }
         | 
| 208 232 | 
             
                .profiler-results.profiler-top.profiler-left {
         | 
| 209 233 | 
             
                  left: 0px; }
         | 
| 210 | 
            -
                  .profiler-results.profiler-top.profiler-left.profiler-no-controls .profiler-totals,
         | 
| 211 | 
            -
                  .profiler-results.profiler-top.profiler-left.profiler-no-controls .profiler-result:last-child .profiler-button,
         | 
| 234 | 
            +
                  .profiler-results.profiler-top.profiler-left.profiler-no-controls .profiler-totals, .profiler-results.profiler-top.profiler-left.profiler-no-controls .profiler-result:last-child .profiler-button,
         | 
| 212 235 | 
             
                  .profiler-results.profiler-top.profiler-left .profiler-controls {
         | 
| 213 236 | 
             
                    -webkit-border-bottom-right-radius: 10px;
         | 
| 214 237 | 
             
                    -moz-border-radius-bottomright: 10px;
         | 
| @@ -218,8 +241,7 @@ | |
| 218 241 | 
             
                    border-right: 1px solid #888; }
         | 
| 219 242 | 
             
                .profiler-results.profiler-top.profiler-right {
         | 
| 220 243 | 
             
                  right: 0px; }
         | 
| 221 | 
            -
                  .profiler-results.profiler-top.profiler-right.profiler-no-controls .profiler-totals,
         | 
| 222 | 
            -
                  .profiler-results.profiler-top.profiler-right.profiler-no-controls .profiler-result:last-child .profiler-button,
         | 
| 244 | 
            +
                  .profiler-results.profiler-top.profiler-right.profiler-no-controls .profiler-totals, .profiler-results.profiler-top.profiler-right.profiler-no-controls .profiler-result:last-child .profiler-button,
         | 
| 223 245 | 
             
                  .profiler-results.profiler-top.profiler-right .profiler-controls {
         | 
| 224 246 | 
             
                    -webkit-border-bottom-left-radius: 10px;
         | 
| 225 247 | 
             
                    -moz-border-radius-bottomleft: 10px;
         | 
| @@ -231,8 +253,7 @@ | |
| 231 253 | 
             
                bottom: 0px; }
         | 
| 232 254 | 
             
                .profiler-results.profiler-bottom.profiler-left {
         | 
| 233 255 | 
             
                  left: 0px; }
         | 
| 234 | 
            -
                  .profiler-results.profiler-bottom.profiler-left.profiler-no-controls .profiler-totals,
         | 
| 235 | 
            -
                  .profiler-results.profiler-bottom.profiler-left.profiler-no-controls .profiler-result:first-child .profiler-button,
         | 
| 256 | 
            +
                  .profiler-results.profiler-bottom.profiler-left.profiler-no-controls .profiler-totals, .profiler-results.profiler-bottom.profiler-left.profiler-no-controls .profiler-result:first-child .profiler-button,
         | 
| 236 257 | 
             
                  .profiler-results.profiler-bottom.profiler-left .profiler-controls {
         | 
| 237 258 | 
             
                    -webkit-border-top-right-radius: 10px;
         | 
| 238 259 | 
             
                    -moz-border-radius-topright: 10px;
         | 
| @@ -242,8 +263,7 @@ | |
| 242 263 | 
             
                    border-right: 1px solid #888; }
         | 
| 243 264 | 
             
                .profiler-results.profiler-bottom.profiler-right {
         | 
| 244 265 | 
             
                  right: 0px; }
         | 
| 245 | 
            -
                  .profiler-results.profiler-bottom.profiler-right.profiler-no-controls .profiler-totals,
         | 
| 246 | 
            -
                  .profiler-results.profiler-bottom.profiler-right.profiler-no-controls .profiler-result:first-child .profiler-button,
         | 
| 266 | 
            +
                  .profiler-results.profiler-bottom.profiler-right.profiler-no-controls .profiler-totals, .profiler-results.profiler-bottom.profiler-right.profiler-no-controls .profiler-result:first-child .profiler-button,
         | 
| 247 267 | 
             
                  .profiler-results.profiler-bottom.profiler-right .profiler-controls {
         | 
| 248 268 | 
             
                    -webkit-border-bottom-top-radius: 10px;
         | 
| 249 269 | 
             
                    -moz-border-radius-topleft: 10px;
         | 
| @@ -349,7 +369,6 @@ | |
| 349 369 | 
             
              @media print {
         | 
| 350 370 | 
             
                .profiler-results {
         | 
| 351 371 | 
             
                  display: none; } }
         | 
| 352 | 
            -
             | 
| 353 372 | 
             
            .profiler-queries-bg {
         | 
| 354 373 | 
             
              z-index: 2147483642;
         | 
| 355 374 | 
             
              display: none;
         | 
| @@ -407,3 +426,15 @@ | |
| 407 426 | 
             
                background: #ffffbb; }
         | 
| 408 427 | 
             
              100% {
         | 
| 409 428 | 
             
                background: #fff; } }
         | 
| 429 | 
            +
             | 
| 430 | 
            +
            .mp-snapshots {
         | 
| 431 | 
            +
              font-family: Helvetica, Arial, sans-serif;
         | 
| 432 | 
            +
              font-size: 16px; }
         | 
| 433 | 
            +
              .mp-snapshots .snapshots-table thead {
         | 
| 434 | 
            +
                background: #6a737c;
         | 
| 435 | 
            +
                color: #ffffff; }
         | 
| 436 | 
            +
              .mp-snapshots .snapshots-table th, .mp-snapshots .snapshots-table td {
         | 
| 437 | 
            +
                padding: 5px;
         | 
| 438 | 
            +
                box-sizing: border-box; }
         | 
| 439 | 
            +
              .mp-snapshots .snapshots-table th {
         | 
| 440 | 
            +
                border-right: 1px solid #ffffff; }
         | 
    
        data/lib/html/includes.js
    CHANGED
    
    | @@ -108,18 +108,8 @@ var MiniProfiler = (function() { | |
| 108 108 | 
             
                      // ie is buggy strip out functions
         | 
| 109 109 | 
             
                      var copy = {
         | 
| 110 110 | 
             
                        navigation: {},
         | 
| 111 | 
            -
                        timing:  | 
| 111 | 
            +
                        timing: clientPerformance.timing.toJSON()
         | 
| 112 112 | 
             
                      };
         | 
| 113 | 
            -
                      var timing = extend({}, clientPerformance.timing);
         | 
| 114 | 
            -
             | 
| 115 | 
            -
                      for (p in timing) {
         | 
| 116 | 
            -
                        if (
         | 
| 117 | 
            -
                          timing.hasOwnProperty(p) &&
         | 
| 118 | 
            -
                          !(typeof timing[p] === "function")
         | 
| 119 | 
            -
                        ) {
         | 
| 120 | 
            -
                          copy.timing[p] = timing[p];
         | 
| 121 | 
            -
                        }
         | 
| 122 | 
            -
                      }
         | 
| 123 113 |  | 
| 124 114 | 
             
                      if (clientPerformance.navigation) {
         | 
| 125 115 | 
             
                        copy.navigation.redirectCount =
         | 
| @@ -147,10 +137,13 @@ var MiniProfiler = (function() { | |
| 147 137 | 
             
                    (function() {
         | 
| 148 138 | 
             
                      var request = new XMLHttpRequest();
         | 
| 149 139 | 
             
                      var url = options.path + "results";
         | 
| 150 | 
            -
                      var params =  | 
| 151 | 
            -
                         | 
| 152 | 
            -
                         | 
| 153 | 
            -
                         | 
| 140 | 
            +
                      var params = {
         | 
| 141 | 
            +
                        id: id,
         | 
| 142 | 
            +
                        clientPerformance: clientPerformance,
         | 
| 143 | 
            +
                        clientProbes: clientProbes,
         | 
| 144 | 
            +
                        popup: 1
         | 
| 145 | 
            +
                      };
         | 
| 146 | 
            +
                      var queryParam = toQueryString(params);
         | 
| 154 147 | 
             
                      request.open("POST", url, true);
         | 
| 155 148 |  | 
| 156 149 | 
             
                      request.onload = function() {
         | 
| @@ -172,24 +165,45 @@ var MiniProfiler = (function() { | |
| 172 165 | 
             
                        "Content-Type",
         | 
| 173 166 | 
             
                        "application/x-www-form-urlencoded"
         | 
| 174 167 | 
             
                      );
         | 
| 175 | 
            -
                      request.send( | 
| 168 | 
            +
                      request.send(queryParam);
         | 
| 176 169 | 
             
                    })();
         | 
| 177 170 | 
             
                  }
         | 
| 178 171 | 
             
                }
         | 
| 179 172 | 
             
              };
         | 
| 180 173 |  | 
| 181 | 
            -
              var  | 
| 182 | 
            -
                 | 
| 183 | 
            -
             | 
| 184 | 
            -
             | 
| 185 | 
            -
                   | 
| 186 | 
            -
             | 
| 187 | 
            -
             | 
| 188 | 
            -
                     | 
| 174 | 
            +
              var toQueryString = function toQueryString(data, parentKey) {
         | 
| 175 | 
            +
                var result = [];
         | 
| 176 | 
            +
                for (var key in data) {
         | 
| 177 | 
            +
                  var val = data[key];
         | 
| 178 | 
            +
                  var newKey = !parentKey ? key : parentKey + "[" + key + "]";
         | 
| 179 | 
            +
                  if (
         | 
| 180 | 
            +
                    typeof val === "object" &&
         | 
| 181 | 
            +
                    !Array.isArray(val) &&
         | 
| 182 | 
            +
                    val !== null &&
         | 
| 183 | 
            +
                    val !== undefined
         | 
| 184 | 
            +
                  ) {
         | 
| 185 | 
            +
                    result[result.length] = toQueryString(val, newKey);
         | 
| 186 | 
            +
                  } else {
         | 
| 187 | 
            +
                    if (Array.isArray(val)) {
         | 
| 188 | 
            +
                      val.forEach(function(v) {
         | 
| 189 | 
            +
                        result[result.length] =
         | 
| 190 | 
            +
                          encodeURIComponent(newKey + "[]") + "=" + encodeURIComponent(v);
         | 
| 191 | 
            +
                      });
         | 
| 192 | 
            +
                    } else if (val === null || val === undefined) {
         | 
| 193 | 
            +
                      result[result.length] = encodeURIComponent(newKey) + "=";
         | 
| 194 | 
            +
                    } else {
         | 
| 195 | 
            +
                      result[result.length] =
         | 
| 196 | 
            +
                        encodeURIComponent(newKey) +
         | 
| 197 | 
            +
                        "=" +
         | 
| 198 | 
            +
                        encodeURIComponent(val.toString());
         | 
| 199 | 
            +
                    }
         | 
| 189 200 | 
             
                  }
         | 
| 190 201 | 
             
                }
         | 
| 191 | 
            -
             | 
| 192 | 
            -
             | 
| 202 | 
            +
                return result
         | 
| 203 | 
            +
                  .filter(function(element) {
         | 
| 204 | 
            +
                    return element && element.length > 0;
         | 
| 205 | 
            +
                  })
         | 
| 206 | 
            +
                  .join("&");
         | 
| 193 207 | 
             
              };
         | 
| 194 208 |  | 
| 195 209 | 
             
              var renderTemplate = function renderTemplate(json) {
         | 
| @@ -659,6 +673,19 @@ var MiniProfiler = (function() { | |
| 659 673 | 
             
                });
         | 
| 660 674 | 
             
              };
         | 
| 661 675 |  | 
| 676 | 
            +
              var initSnapshots = function initSnapshots(dataElement) {
         | 
| 677 | 
            +
                var data = JSON.parse(dataElement.textContent);
         | 
| 678 | 
            +
                var temp = document.createElement("DIV");
         | 
| 679 | 
            +
                if (data.page === "overview") {
         | 
| 680 | 
            +
                  temp.innerHTML = MiniProfiler.templates.snapshotsGroupsList(data);
         | 
| 681 | 
            +
                } else if (data.group_name) {
         | 
| 682 | 
            +
                  temp.innerHTML = MiniProfiler.templates.snapshotsList(data);
         | 
| 683 | 
            +
                }
         | 
| 684 | 
            +
                Array.from(temp.children).forEach(function (child) {
         | 
| 685 | 
            +
                  document.body.appendChild(child);
         | 
| 686 | 
            +
                });
         | 
| 687 | 
            +
              };
         | 
| 688 | 
            +
             | 
| 662 689 | 
             
              var initControls = function initControls(container) {
         | 
| 663 690 | 
             
                if (options.showControls) {
         | 
| 664 691 | 
             
                  var _controls = document.createElement("div");
         | 
| @@ -938,7 +965,7 @@ var MiniProfiler = (function() { | |
| 938 965 | 
             
                  var script = document.getElementById("mini-profiler");
         | 
| 939 966 | 
             
                  if (!script || !script.getAttribute) return;
         | 
| 940 967 |  | 
| 941 | 
            -
                  options = (function() {
         | 
| 968 | 
            +
                  this.options = options = (function() {
         | 
| 942 969 | 
             
                    var version = script.getAttribute("data-version");
         | 
| 943 970 | 
             
                    var path = script.getAttribute("data-path");
         | 
| 944 971 | 
             
                    var currentId = script.getAttribute("data-current-id");
         | 
| @@ -966,6 +993,7 @@ var MiniProfiler = (function() { | |
| 966 993 | 
             
                      script.getAttribute("data-start-hidden") === "true" ||
         | 
| 967 994 | 
             
                      sessionStorage["rack-mini-profiler-start-hidden"] === "true";
         | 
| 968 995 | 
             
                    var htmlContainer = script.getAttribute("data-html-container");
         | 
| 996 | 
            +
                    var cssUrl = script.getAttribute("data-css-url");
         | 
| 969 997 | 
             
                    return {
         | 
| 970 998 | 
             
                      ids: ids,
         | 
| 971 999 | 
             
                      path: path,
         | 
| @@ -982,11 +1010,18 @@ var MiniProfiler = (function() { | |
| 982 1010 | 
             
                      toggleShortcut: toggleShortcut,
         | 
| 983 1011 | 
             
                      startHidden: startHidden,
         | 
| 984 1012 | 
             
                      collapseResults: collapseResults,
         | 
| 985 | 
            -
                      htmlContainer: htmlContainer
         | 
| 1013 | 
            +
                      htmlContainer: htmlContainer,
         | 
| 1014 | 
            +
                      cssUrl: cssUrl
         | 
| 986 1015 | 
             
                    };
         | 
| 987 1016 | 
             
                  })();
         | 
| 988 1017 |  | 
| 989 1018 | 
             
                  var doInit = function doInit() {
         | 
| 1019 | 
            +
                    var snapshotsElement = document.getElementById("snapshots-data");
         | 
| 1020 | 
            +
                    if (snapshotsElement != null) {
         | 
| 1021 | 
            +
                      initSnapshots(snapshotsElement);
         | 
| 1022 | 
            +
                      return;
         | 
| 1023 | 
            +
                    }
         | 
| 1024 | 
            +
             | 
| 990 1025 | 
             
                    // when rendering a shared, full page, this div will exist
         | 
| 991 1026 | 
             
                    container = document.querySelectorAll(".profiler-result-full");
         | 
| 992 1027 |  | 
| @@ -1042,7 +1077,7 @@ var MiniProfiler = (function() { | |
| 1042 1077 |  | 
| 1043 1078 | 
             
                  var init = function init() {
         | 
| 1044 1079 | 
             
                    if (options.authorized) {
         | 
| 1045 | 
            -
                      var url = options. | 
| 1080 | 
            +
                      var url = options.cssUrl;
         | 
| 1046 1081 |  | 
| 1047 1082 | 
             
                      if (document.createStyleSheet) {
         | 
| 1048 1083 | 
             
                        document.createStyleSheet(url);
         | 
| @@ -1380,8 +1415,34 @@ var MiniProfiler = (function() { | |
| 1380 1415 | 
             
                },
         | 
| 1381 1416 | 
             
                showTotalSqlCount: function showTotalSqlCount() {
         | 
| 1382 1417 | 
             
                  return options.showTotalSqlCount;
         | 
| 1418 | 
            +
                },
         | 
| 1419 | 
            +
                timestampToRelative: function timestampToRelative(timestamp) {
         | 
| 1420 | 
            +
                  var now = Math.round((new Date()).getTime() / 1000);
         | 
| 1421 | 
            +
                  timestamp = Math.round(timestamp / 1000);
         | 
| 1422 | 
            +
                  var diff = now - timestamp;
         | 
| 1423 | 
            +
                  if (diff < 60) {
         | 
| 1424 | 
            +
                    return "< 1 minute";
         | 
| 1425 | 
            +
                  }
         | 
| 1426 | 
            +
                  var buildDisplayTime = function buildDisplayTime(num, unit) {
         | 
| 1427 | 
            +
                    var res = num + " " + unit;
         | 
| 1428 | 
            +
                    if (num !== 1) {
         | 
| 1429 | 
            +
                      res += "s";
         | 
| 1430 | 
            +
                    }
         | 
| 1431 | 
            +
                    return res;
         | 
| 1432 | 
            +
                  }
         | 
| 1433 | 
            +
                  diff = Math.round(diff / 60);
         | 
| 1434 | 
            +
                  if (diff <= 60) {
         | 
| 1435 | 
            +
                    return buildDisplayTime(diff, "minute");
         | 
| 1436 | 
            +
                  }
         | 
| 1437 | 
            +
                  diff = Math.round(diff / 60);
         | 
| 1438 | 
            +
                  if (diff <= 24) {
         | 
| 1439 | 
            +
                    return buildDisplayTime(diff, "hour");
         | 
| 1440 | 
            +
                  }
         | 
| 1441 | 
            +
                  diff = Math.round(diff / 24);
         | 
| 1442 | 
            +
                  return buildDisplayTime(diff, "day");
         | 
| 1383 1443 | 
             
                }
         | 
| 1384 1444 | 
             
              };
         | 
| 1385 1445 | 
             
            })();
         | 
| 1386 1446 |  | 
| 1447 | 
            +
            window.MiniProfiler = MiniProfiler;
         | 
| 1387 1448 | 
             
            MiniProfiler.init();
         |