showoff 0.17.1 → 0.17.2
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/README.md +3 -2
- data/lib/showoff.rb +36 -4
- data/lib/showoff/version.rb +1 -1
- data/lib/showoff_utils.rb +5 -4
- data/public/css/images/ui-icons_444444_256x240.png +0 -0
- data/public/css/images/ui-icons_555555_256x240.png +0 -0
- data/public/css/images/ui-icons_777620_256x240.png +0 -0
- data/public/css/images/ui-icons_777777_256x240.png +0 -0
- data/public/css/images/ui-icons_cc0000_256x240.png +0 -0
- data/public/css/images/ui-icons_ffffff_256x240.png +0 -0
- data/public/css/presenter.css +63 -23
- data/public/css/showoff.css +46 -10
- data/public/js/presenter.js +21 -1
- data/public/js/showoff.js +72 -10
- data/views/presenter.erb +11 -0
- metadata +22 -4
- data/public/css/pace.png +0 -0
- data/views/stats copy.erb +0 -70
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: e79f2890ed22c967bf20c13e7086bdd7e6fa441c
         | 
| 4 | 
            +
              data.tar.gz: 5c328c3d8979191809a404b984202d2fca92a8cd
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 281b88028b291de836db367604aaadead708eb79a34f060e1231defc51b188f5dc0cf01f1ceb8ca17e1662528d6f0d168929410f75a880a7c1ca5b24f95c0071
         | 
| 7 | 
            +
              data.tar.gz: 2408b4d8909d3f84df131a7af5069cbc5029d8f768b67d6508d83899ccafec0d549e863554ef4dbfafe921be89395e2013fe505ee1bd77eb900bba1888d66cbe
         | 
    
        data/README.md
    CHANGED
    
    | @@ -64,9 +64,10 @@ you'll need to install both Ruby and the Ruby DevKit for compiling native extens | |
| 64 64 |  | 
| 65 65 | 
             
            ## Documentation
         | 
| 66 66 |  | 
| 67 | 
            -
            Please see the  | 
| 67 | 
            +
            Please see the user manual on the [Showoff homepage](http://puppetlabs.github.io/showoff)
         | 
| 68 | 
            +
            for further information.
         | 
| 68 69 |  | 
| 69 | 
            -
            You can generate a nice & pretty local copy of the  | 
| 70 | 
            +
            You can also generate a nice & pretty local copy of the user manual by running
         | 
| 70 71 | 
             
            `rake doc` in your clone of the repository. The generated HTML will be saved in
         | 
| 71 72 | 
             
            the `docs` directory.
         | 
| 72 73 |  | 
    
        data/lib/showoff.rb
    CHANGED
    
    | @@ -185,6 +185,7 @@ class ShowOff < Sinatra::Application | |
| 185 185 | 
             
                @@cookie    = nil      # presenter cookie. Identifies the presenter for control messages
         | 
| 186 186 | 
             
                @@current   = Hash.new # The current slide that the presenter is viewing
         | 
| 187 187 | 
             
                @@cache     = nil      # Cache slide content for subsequent hits
         | 
| 188 | 
            +
                @@activity  = []       # keep track of completion for activity slides
         | 
| 188 189 |  | 
| 189 190 | 
             
                if @interactive
         | 
| 190 191 | 
             
                  # flush stats to disk periodically
         | 
| @@ -415,6 +416,12 @@ class ShowOff < Sinatra::Application | |
| 415 416 |  | 
| 416 417 | 
             
                    content += sl
         | 
| 417 418 | 
             
                    content += "</div>\n"
         | 
| 419 | 
            +
                    if content_classes.include? 'activity'
         | 
| 420 | 
            +
                      content += '<span class="activityToggle">'
         | 
| 421 | 
            +
                      content += "  <label for=\"activity-#{ref}\">Activity complete</label>"
         | 
| 422 | 
            +
                      content += "  <input type=\"checkbox\" class=\"activity\" name=\"activity-#{ref}\" id=\"activity-#{ref}\">"
         | 
| 423 | 
            +
                      content += '</span>'
         | 
| 424 | 
            +
                    end
         | 
| 418 425 | 
             
                    content += "<canvas class=\"annotations\"></canvas>\n"
         | 
| 419 426 | 
             
                    content += "</div>\n"
         | 
| 420 427 |  | 
| @@ -465,6 +472,9 @@ class ShowOff < Sinatra::Application | |
| 465 472 | 
             
                    result.gsub!(match[0], "<pre class=\"highlight\"><code class=\"#{css}\">#{file}</code></pre>")
         | 
| 466 473 | 
             
                  end
         | 
| 467 474 |  | 
| 475 | 
            +
                  result.gsub!(/\[(fa-.*)\]/, '<i class="fa \1"></i>')
         | 
| 476 | 
            +
             | 
| 477 | 
            +
             | 
| 468 478 | 
             
                  result
         | 
| 469 479 | 
             
                end
         | 
| 470 480 |  | 
| @@ -1111,6 +1121,7 @@ class ShowOff < Sinatra::Application | |
| 1111 1121 | 
             
                  @favicon   = settings.showoff_config['favicon']
         | 
| 1112 1122 | 
             
                  @issues    = settings.showoff_config['issues']
         | 
| 1113 1123 | 
             
                  @edit      = settings.showoff_config['edit'] if @review
         | 
| 1124 | 
            +
                  @feedback  = settings.showoff_config['feedback']
         | 
| 1114 1125 | 
             
                  @@cookie ||= guid()
         | 
| 1115 1126 | 
             
                  response.set_cookie('presenter', @@cookie)
         | 
| 1116 1127 | 
             
                  erb :presenter
         | 
| @@ -1429,10 +1440,10 @@ class ShowOff < Sinatra::Application | |
| 1429 1440 | 
             
                    classes = code.attr('class').split rescue []
         | 
| 1430 1441 | 
             
                    lang    = classes.shift =~ /language-(\S*)/ ? $1 : nil
         | 
| 1431 1442 |  | 
| 1432 | 
            -
                    [lang, code.text, classes]
         | 
| 1443 | 
            +
                    [lang, code.text.gsub(/^\* /, ' '), classes]
         | 
| 1433 1444 | 
             
                  end
         | 
| 1434 1445 | 
             
                else
         | 
| 1435 | 
            -
                  doc.css(classes)[index.to_i].text rescue 'Invalid code block index'
         | 
| 1446 | 
            +
                  doc.css(classes)[index.to_i].text.gsub(/^\* /, ' ') rescue 'Invalid code block index'
         | 
| 1436 1447 | 
             
                end
         | 
| 1437 1448 | 
             
              end
         | 
| 1438 1449 |  | 
| @@ -1577,6 +1588,10 @@ class ShowOff < Sinatra::Application | |
| 1577 1588 | 
             
                # Docs suggest that old versions of Sinatra might provide an array here, so just make sure.
         | 
| 1578 1589 | 
             
                filename = path.class == Array ? path.first : path
         | 
| 1579 1590 | 
             
                @logger.debug "Editing #{filename}"
         | 
| 1591 | 
            +
             | 
| 1592 | 
            +
                # When a relative path is used, it's sometimes fully expanded. But then when
         | 
| 1593 | 
            +
                # it's passed via URL, the initial slash is lost. Here we try to get it back.
         | 
| 1594 | 
            +
                filename = "/#{filename}" unless File.exist? filename
         | 
| 1580 1595 | 
             
                return unless File.exist? filename
         | 
| 1581 1596 |  | 
| 1582 1597 | 
             
                if request.host != 'localhost'
         | 
| @@ -1682,9 +1697,26 @@ class ShowOff < Sinatra::Application | |
| 1682 1697 | 
             
                            @logger.debug "Recorded current slide #{slide} for #{remote}"
         | 
| 1683 1698 | 
             
                          end
         | 
| 1684 1699 |  | 
| 1685 | 
            -
             | 
| 1686 1700 | 
             
                        when 'position'
         | 
| 1687 | 
            -
                          ws.send( { 'current' => @@current[:number] }.to_json ) unless @@cookie.nil?
         | 
| 1701 | 
            +
                          ws.send( { 'message' => 'current', 'current' => @@current[:number] }.to_json ) unless @@cookie.nil?
         | 
| 1702 | 
            +
             | 
| 1703 | 
            +
                        when 'activity'
         | 
| 1704 | 
            +
                          next if valid_presenter_cookie?
         | 
| 1705 | 
            +
                          remote = request.cookies['client_id']
         | 
| 1706 | 
            +
                          slide  = control['slide']
         | 
| 1707 | 
            +
                          status = control['status']
         | 
| 1708 | 
            +
                          @@activity[slide] ||= {}
         | 
| 1709 | 
            +
                          @@activity[slide][remote] = status
         | 
| 1710 | 
            +
             | 
| 1711 | 
            +
                          current  = @@current[:number]
         | 
| 1712 | 
            +
                          activity = @@activity[current] rescue nil
         | 
| 1713 | 
            +
             | 
| 1714 | 
            +
                          @logger.debug "Current activity status: #{activity.inspect}"
         | 
| 1715 | 
            +
                          if activity
         | 
| 1716 | 
            +
                            # select all activity on this slide where completion status is false
         | 
| 1717 | 
            +
                            count = activity.select {|viewer, status| status == false }.size
         | 
| 1718 | 
            +
                            EM.next_tick { settings.presenters.each{|s| s.send({ 'message' => 'activity', 'count' => count }.to_json) } }
         | 
| 1719 | 
            +
                          end
         | 
| 1688 1720 |  | 
| 1689 1721 | 
             
                        when 'pace', 'question', 'cancel'
         | 
| 1690 1722 | 
             
                          # just forward to the presenter(s) along with a debounce in case a presenter is registered twice
         | 
    
        data/lib/showoff/version.rb
    CHANGED
    
    
    
        data/lib/showoff_utils.rb
    CHANGED
    
    | @@ -470,14 +470,15 @@ class ShowOffUtils | |
| 470 470 | 
             
                end.flatten.compact.each do |filename|
         | 
| 471 471 | 
             
                  # We do this in two passes simply because most of it was already done
         | 
| 472 472 | 
             
                  # and I don't want to waste time on legacy functionality.
         | 
| 473 | 
            -
                  path = File.dirname(filename)
         | 
| 474 | 
            -
                  sections[path] ||= []
         | 
| 475 | 
            -
             | 
| 476 473 | 
             
                  if File.directory? filename
         | 
| 474 | 
            +
                    path = filename
         | 
| 475 | 
            +
                    sections[path] ||= []
         | 
| 477 476 | 
             
                    Dir.glob("#{filename}/**/*.md").sort.each do |slidefile|
         | 
| 478 | 
            -
                      sections[path] | 
| 477 | 
            +
                      sections[path] << slidefile
         | 
| 479 478 | 
             
                    end
         | 
| 480 479 | 
             
                  else
         | 
| 480 | 
            +
                    path = File.dirname(filename)
         | 
| 481 | 
            +
                    sections[path] ||= []
         | 
| 481 482 | 
             
                    sections[path]  << filename
         | 
| 482 483 | 
             
                  end
         | 
| 483 484 | 
             
                end
         | 
| Binary file | 
| Binary file | 
| Binary file | 
| Binary file | 
| Binary file | 
| Binary file | 
    
        data/public/css/presenter.css
    CHANGED
    
    | @@ -83,7 +83,7 @@ | |
| 83 83 | 
             
                border: none;
         | 
| 84 84 | 
             
                border-radius: 0;
         | 
| 85 85 | 
             
                line-height: 3em;
         | 
| 86 | 
            -
                 | 
| 86 | 
            +
                vertical-align: inherit; /* overrides a jquery-ui style */
         | 
| 87 87 | 
             
              }
         | 
| 88 88 |  | 
| 89 89 | 
             
              #links a.enabled,
         | 
| @@ -208,30 +208,62 @@ | |
| 208 208 | 
             
                  height: 40px;
         | 
| 209 209 | 
             
                  min-height: 40px;
         | 
| 210 210 | 
             
                  position: relative;
         | 
| 211 | 
            -
                   | 
| 212 | 
            -
                }
         | 
| 213 | 
            -
                #paceFast,
         | 
| 214 | 
            -
                #paceSlow {
         | 
| 215 | 
            -
                  display: none;
         | 
| 216 | 
            -
                }
         | 
| 217 | 
            -
                #paceFast {
         | 
| 218 | 
            -
                  float: left;
         | 
| 219 | 
            -
                  margin-left: 1em;
         | 
| 220 | 
            -
                }
         | 
| 221 | 
            -
                #paceSlow {
         | 
| 222 | 
            -
                  float: right;
         | 
| 223 | 
            -
                  margin-right: 1em;
         | 
| 211 | 
            +
                  border-bottom: 1px solid #ccc;
         | 
| 224 212 | 
             
                }
         | 
| 225 | 
            -
             | 
| 226 | 
            -
             | 
| 227 | 
            -
             | 
| 228 | 
            -
             | 
| 229 | 
            -
             | 
| 230 | 
            -
             | 
| 231 | 
            -
             | 
| 232 | 
            -
             | 
| 213 | 
            +
                  #feedbackPace .gradient {
         | 
| 214 | 
            +
                    position: absolute;
         | 
| 215 | 
            +
                    bottom: 0;
         | 
| 216 | 
            +
                    width: 100%;
         | 
| 217 | 
            +
                    height: 20px;
         | 
| 218 | 
            +
                    background-color:#ff0000;
         | 
| 219 | 
            +
                    filter:progid:DXImageTransform.Microsoft.gradient(GradientType=1,startColorstr=#ff0000, endColorstr=#eeff00);
         | 
| 220 | 
            +
                    background-image:-moz-linear-gradient(left, #ff0000 10%, #eeff00 30%,#00ff00 50%,#eeff00 70%,#ff0000 90%);
         | 
| 221 | 
            +
                    background-image:linear-gradient(left, #ff0000 10%, #eeff00 30%,#00ff00 50%,#eeff00 70%,#ff0000 90%);
         | 
| 222 | 
            +
                    background-image:-webkit-linear-gradient(left, #ff0000 10%, #eeff00 30%,#00ff00 50%,#eeff00 70%,#ff0000 90%);
         | 
| 223 | 
            +
                    background-image:-o-linear-gradient(left, #ff0000 10%, #eeff00 30%,#00ff00 50%,#eeff00 70%,#ff0000 90%);
         | 
| 224 | 
            +
                    background-image:-ms-linear-gradient(left, #ff0000 10%, #eeff00 30%,#00ff00 50%,#eeff00 70%,#ff0000 90%);
         | 
| 225 | 
            +
                    background-image:-webkit-gradient(linear, left bottom, right bottom, color-stop(10%,#ff0000), color-stop(30%,#eeff00),color-stop(50%,#00ff00),color-stop(70%,#eeff00),color-stop(90%,#ff0000));
         | 
| 226 | 
            +
                  }
         | 
| 227 | 
            +
                  #feedbackPace .obscure {
         | 
| 228 | 
            +
                    position: absolute;
         | 
| 229 | 
            +
                    background-color: #f0f0f0;
         | 
| 230 | 
            +
                    bottom: 0;
         | 
| 231 | 
            +
                    height: 20px;
         | 
| 232 | 
            +
                    transition: width 0.25s;
         | 
| 233 | 
            +
                  }
         | 
| 234 | 
            +
                  #feedbackPace .obscure.left {
         | 
| 235 | 
            +
                    left: 0;
         | 
| 236 | 
            +
                    width: 50%;
         | 
| 237 | 
            +
                    border-right: 1px solid black;
         | 
| 238 | 
            +
                  }
         | 
| 239 | 
            +
                  #feedbackPace .obscure.right {
         | 
| 240 | 
            +
                    right: 0;
         | 
| 241 | 
            +
                    width: 50%;
         | 
| 242 | 
            +
                    border-left: 1px solid black;
         | 
| 243 | 
            +
                  }
         | 
| 233 244 |  | 
| 234 | 
            -
             | 
| 245 | 
            +
                  #paceFast,
         | 
| 246 | 
            +
                  #paceSlow {
         | 
| 247 | 
            +
                    display: none;
         | 
| 248 | 
            +
                  }
         | 
| 249 | 
            +
                  #paceFast {
         | 
| 250 | 
            +
                    float: left;
         | 
| 251 | 
            +
                    margin-left: 1em;
         | 
| 252 | 
            +
                  }
         | 
| 253 | 
            +
                  #paceSlow {
         | 
| 254 | 
            +
                    float: right;
         | 
| 255 | 
            +
                    margin-right: 1em;
         | 
| 256 | 
            +
                  }
         | 
| 257 | 
            +
                  #paceMarker {
         | 
| 258 | 
            +
                    left: 50%;
         | 
| 259 | 
            +
                    position: absolute;
         | 
| 260 | 
            +
                    transform: translate(-50%, -50%);
         | 
| 261 | 
            +
                    -webkit-transform: translate(-50%, 0);
         | 
| 262 | 
            +
                    -moz-transform: translate(-50%, 0);
         | 
| 263 | 
            +
                    -ms-transform: translate(-50%, 0);
         | 
| 264 | 
            +
                    -o-transform: translate(-50%, 0);
         | 
| 265 | 
            +
                    transition: left 0.25s;
         | 
| 266 | 
            +
                  }
         | 
| 235 267 |  | 
| 236 268 | 
             
                .submenu {
         | 
| 237 269 | 
             
                  flex-grow: 10;
         | 
| @@ -574,6 +606,7 @@ | |
| 574 606 | 
             
                width: 90%;
         | 
| 575 607 | 
             
              }
         | 
| 576 608 |  | 
| 609 | 
            +
              .slide.activity .count,
         | 
| 577 610 | 
             
              #notes .count {
         | 
| 578 611 | 
             
                display: inline-block;
         | 
| 579 612 | 
             
                background-color: #2e2e2e;
         | 
| @@ -604,6 +637,13 @@ | |
| 604 637 | 
             
                display: none;
         | 
| 605 638 | 
             
              }
         | 
| 606 639 |  | 
| 640 | 
            +
            .slide.activity .count {
         | 
| 641 | 
            +
              position: absolute;
         | 
| 642 | 
            +
              bottom: .25em;
         | 
| 643 | 
            +
              right: .5em;
         | 
| 644 | 
            +
              padding: 0 0.25em;
         | 
| 645 | 
            +
            }
         | 
| 646 | 
            +
             | 
| 607 647 |  | 
| 608 648 | 
             
            a.controls {
         | 
| 609 649 | 
             
              text-decoration: none;
         | 
    
        data/public/css/showoff.css
    CHANGED
    
    | @@ -382,6 +382,10 @@ img#disconnected { | |
| 382 382 | 
             
              margin-right: 8px;
         | 
| 383 383 | 
             
            }
         | 
| 384 384 |  | 
| 385 | 
            +
            .sideMenu hr {
         | 
| 386 | 
            +
              clear: both;
         | 
| 387 | 
            +
            }
         | 
| 388 | 
            +
             | 
| 385 389 | 
             
            .buttonWrapper {
         | 
| 386 390 | 
             
              line-height: 48px;
         | 
| 387 391 | 
             
              padding: 0 16px;
         | 
| @@ -577,6 +581,12 @@ pre.highlight code.language-powershellconsole.nochrome { | |
| 577 581 | 
             
              border-radius: 4px;
         | 
| 578 582 | 
             
            }
         | 
| 579 583 |  | 
| 584 | 
            +
            pre.highlight code .highlightedLine {
         | 
| 585 | 
            +
              background-color: #f5e2e2;
         | 
| 586 | 
            +
              display: inline-block;
         | 
| 587 | 
            +
              width: 100%;
         | 
| 588 | 
            +
            }
         | 
| 589 | 
            +
             | 
| 580 590 | 
             
            /* to avoid breaking changes */
         | 
| 581 591 | 
             
            .highlight .language-shell {
         | 
| 582 592 | 
             
              display: block;
         | 
| @@ -747,14 +757,6 @@ form .element { | |
| 747 757 | 
             
            /*****************
         | 
| 748 758 | 
             
             ***   modals   ***
         | 
| 749 759 | 
             
             *****************/
         | 
| 750 | 
            -
            #help-modal {
         | 
| 751 | 
            -
              font-size: .9em;
         | 
| 752 | 
            -
            }
         | 
| 753 | 
            -
             | 
| 754 | 
            -
            #help-modal .ui-dialog .ui-dialog-title {
         | 
| 755 | 
            -
              font-size: 1em;
         | 
| 756 | 
            -
            }
         | 
| 757 | 
            -
             | 
| 758 760 | 
             
            .ui-dialog.ui-widget-content {
         | 
| 759 761 | 
             
              border: none;
         | 
| 760 762 | 
             
              box-shadow:0 0 25px rgba(0,0,0,0.35);
         | 
| @@ -812,6 +814,15 @@ form .element { | |
| 812 814 | 
             
              display: none;
         | 
| 813 815 | 
             
            }
         | 
| 814 816 |  | 
| 817 | 
            +
            #help-modal {
         | 
| 818 | 
            +
              display: none;
         | 
| 819 | 
            +
              font-size: .9em;
         | 
| 820 | 
            +
            }
         | 
| 821 | 
            +
             | 
| 822 | 
            +
            #help-modal .ui-dialog .ui-dialog-title {
         | 
| 823 | 
            +
              font-size: 1em;
         | 
| 824 | 
            +
            }
         | 
| 825 | 
            +
             | 
| 815 826 | 
             
            #help div {
         | 
| 816 827 | 
             
              padding: 6px 0;
         | 
| 817 828 | 
             
            }
         | 
| @@ -1041,6 +1052,7 @@ form .element { | |
| 1041 1052 |  | 
| 1042 1053 | 
             
            .callout {
         | 
| 1043 1054 | 
             
              padding: 1em;
         | 
| 1055 | 
            +
              position: relative;
         | 
| 1044 1056 | 
             
              line-height: 1.2em;
         | 
| 1045 1057 | 
             
              border: 1px solid #222;
         | 
| 1046 1058 | 
             
              border-radius: 4px;
         | 
| @@ -1054,7 +1066,18 @@ form .element { | |
| 1054 1066 | 
             
                font-size: 2em;
         | 
| 1055 1067 | 
             
                text-decoration: inherit;
         | 
| 1056 1068 | 
             
                position: absolute;
         | 
| 1057 | 
            -
                left:  | 
| 1069 | 
            +
                left: 0.3em;
         | 
| 1070 | 
            +
              }
         | 
| 1071 | 
            +
              .callout:after {
         | 
| 1072 | 
            +
                font-family: FontAwesome;
         | 
| 1073 | 
            +
                font-style: normal;
         | 
| 1074 | 
            +
                font-weight: normal;
         | 
| 1075 | 
            +
                font-size: 1.5em;
         | 
| 1076 | 
            +
                text-decoration: inherit;
         | 
| 1077 | 
            +
                position: absolute;
         | 
| 1078 | 
            +
                left: 0.5em;
         | 
| 1079 | 
            +
                top: 0.65em;
         | 
| 1080 | 
            +
                color: #fff;
         | 
| 1058 1081 | 
             
              }
         | 
| 1059 1082 |  | 
| 1060 1083 | 
             
              .callout.info,
         | 
| @@ -1064,7 +1087,8 @@ form .element { | |
| 1064 1087 | 
             
              .callout.stop,
         | 
| 1065 1088 | 
             
              .callout.thumbsup,
         | 
| 1066 1089 | 
             
              .callout.conversation,
         | 
| 1067 | 
            -
              .callout.glossary | 
| 1090 | 
            +
              .callout.glossary,
         | 
| 1091 | 
            +
              .callout.terminal {
         | 
| 1068 1092 | 
             
                padding-left: 3em;
         | 
| 1069 1093 | 
             
              }
         | 
| 1070 1094 |  | 
| @@ -1077,6 +1101,10 @@ form .element { | |
| 1077 1101 | 
             
              .callout.conversation:before { content: "\f0e5"; } /* fa-comment */
         | 
| 1078 1102 | 
             
              .callout.glossary:before     { content: "\f02d"; } /* fa-book */
         | 
| 1079 1103 |  | 
| 1104 | 
            +
              .callout.terminal:before     { content: "\f0c8"; } /* fa-square */
         | 
| 1105 | 
            +
              .callout.terminal:after      { content: "\f120"; } /* fa-terminal */
         | 
| 1106 | 
            +
             | 
| 1107 | 
            +
             | 
| 1080 1108 | 
             
            /* callouts used on error pages */
         | 
| 1081 1109 | 
             
            .error .callout {
         | 
| 1082 1110 | 
             
              padding-left: 4em;
         | 
| @@ -1117,6 +1145,14 @@ a.term:after { | |
| 1117 1145 | 
             
            ***  glossary       ***
         | 
| 1118 1146 | 
             
            **********************/
         | 
| 1119 1147 |  | 
| 1148 | 
            +
            /**************************
         | 
| 1149 | 
            +
            *** activity indicators ***
         | 
| 1150 | 
            +
            ***************************/
         | 
| 1151 | 
            +
            .slide.activity .activityToggle {
         | 
| 1152 | 
            +
              position: absolute;
         | 
| 1153 | 
            +
              bottom: 1em;
         | 
| 1154 | 
            +
              right: 1em;
         | 
| 1155 | 
            +
            }
         | 
| 1120 1156 |  | 
| 1121 1157 | 
             
            /* Render hidden headlines so that when we print with wkhtmltopdf, we can use section
         | 
| 1122 1158 | 
             
               titles for page headers.  it would make sense to put this in the print section, but
         | 
    
        data/public/js/presenter.js
    CHANGED
    
    | @@ -30,7 +30,10 @@ $(document).ready(function(){ | |
| 30 30 | 
             
              $("#settings").click(      function() { $("#settings-modal").dialog("open"); });
         | 
| 31 31 | 
             
              $("#slideSource a").click( function() { openEditor() });
         | 
| 32 32 | 
             
              $("#notesToggle").click(   function() { toggleNotes() });
         | 
| 33 | 
            -
              $("#clearCookies").click(  function() { | 
| 33 | 
            +
              $("#clearCookies").click(  function() {
         | 
| 34 | 
            +
                clearCookies();
         | 
| 35 | 
            +
                location.reload(false);
         | 
| 36 | 
            +
              });
         | 
| 34 37 | 
             
              $("#nextWinCancel").click( function() { chooseLayout('default') });
         | 
| 35 38 | 
             
              $("#openNextWindow").click(function() { openNext() });
         | 
| 36 39 |  | 
| @@ -514,6 +517,15 @@ function updatePace() { | |
| 514 517 | 
             
              var position = Math.max(Math.min(sum, 90), 10); // between 10 and 90
         | 
| 515 518 | 
             
              $("#paceMarker").css({ left: position+"%" });
         | 
| 516 519 |  | 
| 520 | 
            +
              if (position > 50) {
         | 
| 521 | 
            +
                $("#feedbackPace .obscure.left").css({ width: "50%" });
         | 
| 522 | 
            +
                $("#feedbackPace .obscure.right").css({ width: (100-position)+"%" });
         | 
| 523 | 
            +
              }
         | 
| 524 | 
            +
              else {
         | 
| 525 | 
            +
                $("#feedbackPace .obscure.right").css({ width: "50%" });
         | 
| 526 | 
            +
                $("#feedbackPace .obscure.left").css({ width: position+"%" });
         | 
| 527 | 
            +
              }
         | 
| 528 | 
            +
             | 
| 517 529 | 
             
              if(position > 75) {
         | 
| 518 530 | 
             
                $("#paceFast").show();
         | 
| 519 531 | 
             
              } else {
         | 
| @@ -526,6 +538,10 @@ function updatePace() { | |
| 526 538 | 
             
              }
         | 
| 527 539 | 
             
            }
         | 
| 528 540 |  | 
| 541 | 
            +
            function updateActivityCompletion(count) {
         | 
| 542 | 
            +
              currentSlide.children('.count').text(count);
         | 
| 543 | 
            +
            }
         | 
| 544 | 
            +
             | 
| 529 545 | 
             
            // extend this function to add presenter bits
         | 
| 530 546 | 
             
            var origGotoSlide = gotoSlide;
         | 
| 531 547 | 
             
            gotoSlide = function (slideNum)
         | 
| @@ -663,6 +679,10 @@ function postSlide() { | |
| 663 679 | 
             
                $("#notes div.form.wrapper").each(function(e) {
         | 
| 664 680 | 
             
                  renderFormInterval = renderFormWatcher($(this));
         | 
| 665 681 | 
             
                });
         | 
| 682 | 
            +
             | 
| 683 | 
            +
                if(currentSlide.hasClass('activity')) {
         | 
| 684 | 
            +
                  currentSlide.children('.activityToggle').replaceWith('<span class="count">0</span>');
         | 
| 685 | 
            +
                }
         | 
| 666 686 | 
             
            	}
         | 
| 667 687 | 
             
            }
         | 
| 668 688 |  | 
    
        data/public/js/showoff.js
    CHANGED
    
    | @@ -19,6 +19,7 @@ var lastMessageGuid = 0 | |
| 19 19 | 
             
            var query;
         | 
| 20 20 | 
             
            var section = 'handouts'; // default to showing handout notes for display view
         | 
| 21 21 | 
             
            var slideStartTime = new Date().getTime()
         | 
| 22 | 
            +
            var activityIncomplete = false; // slides won't advance when this is on
         | 
| 22 23 |  | 
| 23 24 | 
             
            var loadSlidesBool
         | 
| 24 25 | 
             
            var loadSlidesPrefix
         | 
| @@ -119,9 +120,8 @@ function setupPreso(load_slides, prefix) { | |
| 119 120 | 
             
              });
         | 
| 120 121 |  | 
| 121 122 | 
             
              // Open up our control socket
         | 
| 122 | 
            -
               | 
| 123 | 
            -
             | 
| 124 | 
            -
              }
         | 
| 123 | 
            +
              connectControlChannel();
         | 
| 124 | 
            +
             | 
| 125 125 | 
             
            }
         | 
| 126 126 |  | 
| 127 127 | 
             
            function loadSlides(load_slides, prefix, reload, hard) {
         | 
| @@ -183,6 +183,16 @@ function initializePresentation(prefix) { | |
| 183 183 | 
             
              $('pre.highlight code').each(function(i, block) {
         | 
| 184 184 | 
             
                try {
         | 
| 185 185 | 
             
                  hljs.highlightBlock(block);
         | 
| 186 | 
            +
             | 
| 187 | 
            +
                  // Highlight requested lines
         | 
| 188 | 
            +
                  block.innerHTML = block.innerHTML.split(/\r?\n/).map(function (line, i) {
         | 
| 189 | 
            +
                    if (line.indexOf('* ') === 0) {
         | 
| 190 | 
            +
                      return line.replace(/^\*(.*)$/, '<div class="highlightedLine">$1</div>');
         | 
| 191 | 
            +
                    }
         | 
| 192 | 
            +
             | 
| 193 | 
            +
                    return line;
         | 
| 194 | 
            +
                  }).join('\n');
         | 
| 195 | 
            +
             | 
| 186 196 | 
             
                } catch(e) {
         | 
| 187 197 | 
             
                  console.log('Syntax highlighting failed on ' + $(this).closest('div.slide').attr('id'));
         | 
| 188 198 | 
             
                  console.log('Syntax highlighting failed for ' + $(this).attr('class'));
         | 
| @@ -218,6 +228,13 @@ function initializePresentation(prefix) { | |
| 218 228 | 
             
                }
         | 
| 219 229 | 
             
              });
         | 
| 220 230 |  | 
| 231 | 
            +
              // The display window doesn't need the extra chrome
         | 
| 232 | 
            +
              if(typeof(presenterView) != 'undefined') {
         | 
| 233 | 
            +
                $('.slide.activity').removeClass('activity').children('.activityToggle').remove();
         | 
| 234 | 
            +
              }
         | 
| 235 | 
            +
              $('.slide.activity .activityToggle input.activity').checkboxradio();
         | 
| 236 | 
            +
              $('.slide.activity .activityToggle input.activity').change(toggleComplete);
         | 
| 237 | 
            +
             | 
| 221 238 | 
             
              // initialize mermaid, but don't render yet since the slide sizes are indeterminate
         | 
| 222 239 | 
             
              mermaid.initialize({startOnLoad:false});
         | 
| 223 240 |  | 
| @@ -240,15 +257,15 @@ function zoom(presenter) { | |
| 240 257 | 
             
              }
         | 
| 241 258 |  | 
| 242 259 | 
             
              // Calculate margins to center the thing *before* scaling
         | 
| 243 | 
            -
              //  | 
| 244 | 
            -
              if( | 
| 260 | 
            +
              // Vertically center on presenter, top align everywhere else
         | 
| 261 | 
            +
              if(presenter) {
         | 
| 262 | 
            +
                var hMargin = (hBody - hSlide) /2;
         | 
| 263 | 
            +
              }
         | 
| 264 | 
            +
              else {
         | 
| 245 265 | 
             
                // (center of slide to top) - (half of the zoomed slide)
         | 
| 246 266 | 
             
                //var hMargin = (hSlide/2 * newZoom) - (hSlide / 2);
         | 
| 247 267 | 
             
                var hMargin = (hSlide * newZoom - hSlide) / 2;
         | 
| 248 268 | 
             
              }
         | 
| 249 | 
            -
              else {
         | 
| 250 | 
            -
                var hMargin = (hBody - hSlide) /2;
         | 
| 251 | 
            -
              }
         | 
| 252 269 | 
             
              var wMargin = (wBody - wSlide) /2;
         | 
| 253 270 |  | 
| 254 271 | 
             
              preso.css("margin", hMargin + "px " + wMargin + "px");
         | 
| @@ -642,6 +659,21 @@ function showSlide(back_step, updatepv) { | |
| 642 659 | 
             
              // copy notes to the notes field for mobile.
         | 
| 643 660 | 
             
              postSlide();
         | 
| 644 661 |  | 
| 662 | 
            +
              // is this an activity slide that has not yet been marked complete?
         | 
| 663 | 
            +
              if (currentSlide.hasClass('activity')) {
         | 
| 664 | 
            +
                 if (currentSlide.find('input.activity').is(":checked")) {
         | 
| 665 | 
            +
                  activityIncomplete = false;
         | 
| 666 | 
            +
                  sendActivityStatus(true);
         | 
| 667 | 
            +
                }
         | 
| 668 | 
            +
                else {
         | 
| 669 | 
            +
                  activityIncomplete = true;
         | 
| 670 | 
            +
                  sendActivityStatus(false);
         | 
| 671 | 
            +
                }
         | 
| 672 | 
            +
              }
         | 
| 673 | 
            +
              else {
         | 
| 674 | 
            +
                activityIncomplete = false;
         | 
| 675 | 
            +
              }
         | 
| 676 | 
            +
             | 
| 645 677 | 
             
              // make all bigly text tremendous
         | 
| 646 678 | 
             
              currentSlide.children('.content.bigtext').bigtext();
         | 
| 647 679 |  | 
| @@ -730,6 +762,10 @@ function submitForm(form) { | |
| 730 762 | 
             
                  var submit = form.find("input[type=submit]")
         | 
| 731 763 | 
             
                  submit.attr("disabled", "disabled");
         | 
| 732 764 | 
             
                  submit.removeClass("dirty");
         | 
| 765 | 
            +
             | 
| 766 | 
            +
                  // stop blocking follow mode
         | 
| 767 | 
            +
                  activityIncomplete = false;
         | 
| 768 | 
            +
                  getPosition();
         | 
| 733 769 | 
             
                });
         | 
| 734 770 | 
             
              }
         | 
| 735 771 | 
             
            }
         | 
| @@ -758,7 +794,10 @@ function validateForm(form) { | |
| 758 794 | 
             
            function enableForm(element) {
         | 
| 759 795 | 
             
              var submit = element.closest('form').find(':submit')
         | 
| 760 796 | 
             
              submit.removeAttr("disabled");
         | 
| 761 | 
            -
              submit.addClass("dirty")
         | 
| 797 | 
            +
              submit.addClass("dirty");
         | 
| 798 | 
            +
             | 
| 799 | 
            +
              // once a form is started, stop following the presenter
         | 
| 800 | 
            +
              activityIncomplete = true;
         | 
| 762 801 | 
             
            }
         | 
| 763 802 |  | 
| 764 803 | 
             
            function renderFormWatcher(element) {
         | 
| @@ -996,6 +1035,9 @@ function parseMessage(data) { | |
| 996 1035 | 
             
                    removeQuestion(command["questionID"]);
         | 
| 997 1036 | 
             
                    break;
         | 
| 998 1037 |  | 
| 1038 | 
            +
                  case 'activity':
         | 
| 1039 | 
            +
                    updateActivityCompletion(command['count']);
         | 
| 1040 | 
            +
             | 
| 999 1041 | 
             
                  case 'annotation':
         | 
| 1000 1042 | 
             
                    invokeAnnotation(command["type"], command["x"], command["y"]);
         | 
| 1001 1043 | 
             
                    break;
         | 
| @@ -1063,6 +1105,12 @@ function sendAnnotationConfig(setting, value) { | |
| 1063 1105 | 
             
              }
         | 
| 1064 1106 | 
             
            }
         | 
| 1065 1107 |  | 
| 1108 | 
            +
            function sendActivityStatus(status) {
         | 
| 1109 | 
            +
              if (ws.readyState == WebSocket.OPEN) {
         | 
| 1110 | 
            +
                ws.send(JSON.stringify({ message: 'activity', slide: slidenum, status: status }));
         | 
| 1111 | 
            +
              }
         | 
| 1112 | 
            +
            }
         | 
| 1113 | 
            +
             | 
| 1066 1114 | 
             
            function invokeAnnotation(type, x, y) {
         | 
| 1067 1115 | 
             
              switch (type) {
         | 
| 1068 1116 | 
             
                case 'erase':
         | 
| @@ -1114,7 +1162,7 @@ function editSlide() { | |
| 1114 1162 | 
             
            }
         | 
| 1115 1163 |  | 
| 1116 1164 | 
             
            function follow(slide, newIncrement) {
         | 
| 1117 | 
            -
              if (mode.follow) {
         | 
| 1165 | 
            +
              if (mode.follow && ! activityIncomplete) {
         | 
| 1118 1166 | 
             
                var lastSlide = slidenum;
         | 
| 1119 1167 | 
             
                console.log("New slide: " + slide);
         | 
| 1120 1168 | 
             
                gotoSlide(slide);
         | 
| @@ -1360,6 +1408,20 @@ function getKeyName (event) { | |
| 1360 1408 | 
             
              return keyName;
         | 
| 1361 1409 | 
             
            }
         | 
| 1362 1410 |  | 
| 1411 | 
            +
            function toggleComplete() {
         | 
| 1412 | 
            +
              if($(this).is(':checked')) {
         | 
| 1413 | 
            +
                activityIncomplete = false;
         | 
| 1414 | 
            +
                sendActivityStatus(true);
         | 
| 1415 | 
            +
                if(mode.follow) {
         | 
| 1416 | 
            +
                  getPosition();
         | 
| 1417 | 
            +
                }
         | 
| 1418 | 
            +
              }
         | 
| 1419 | 
            +
              else {
         | 
| 1420 | 
            +
                activityIncomplete = true;
         | 
| 1421 | 
            +
                sendActivityStatus(false);
         | 
| 1422 | 
            +
              }
         | 
| 1423 | 
            +
            }
         | 
| 1424 | 
            +
             | 
| 1363 1425 | 
             
            function toggleDebug () {
         | 
| 1364 1426 | 
             
              debugMode = !debugMode;
         | 
| 1365 1427 | 
             
              doDebugStuff();
         | 
    
        data/views/presenter.erb
    CHANGED
    
    | @@ -64,18 +64,29 @@ | |
| 64 64 | 
             
                    </span>
         | 
| 65 65 | 
             
                    <div id="timerDisplay"></div>
         | 
| 66 66 | 
             
                  </div>
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                  <% if @feedback then %>
         | 
| 67 69 | 
             
                  <div id="feedbackPace">
         | 
| 70 | 
            +
                    <div class="gradient"> </div>
         | 
| 71 | 
            +
                    <div class="left obscure"> </div>
         | 
| 72 | 
            +
                    <div class="right obscure"> </div>
         | 
| 68 73 | 
             
                    <span id="paceSlow">Speed Up!</span>
         | 
| 69 74 | 
             
                    <span id="paceFast">Slow Down!</span>
         | 
| 70 75 | 
             
                    <img id="paceMarker" src="<%= @asset_path %>/css/paceMarker.png" />
         | 
| 71 76 | 
             
                  </div>
         | 
| 77 | 
            +
                  <% end %>
         | 
| 78 | 
            +
             | 
| 72 79 | 
             
                  <div id="navigation" class="submenu"></div>
         | 
| 73 80 | 
             
                  <div id="navigationHover"></div>
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                  <% if @feedback then %>
         | 
| 74 83 | 
             
                  <div id="questions">
         | 
| 75 84 | 
             
                    <h3>Audience Questions</h3>
         | 
| 76 85 | 
             
                    <ol id="unanswered"></ol>
         | 
| 77 86 | 
             
                    <ol id="answered"></ol>
         | 
| 78 87 | 
             
                  </div>
         | 
| 88 | 
            +
                  <% end %>
         | 
| 89 | 
            +
             | 
| 79 90 | 
             
                </div>
         | 
| 80 91 |  | 
| 81 92 | 
             
                <div id="presenter">
         | 
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: showoff
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0.17. | 
| 4 | 
            +
              version: 0.17.2
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Scott Chacon
         | 
| @@ -9,7 +9,7 @@ authors: | |
| 9 9 | 
             
            autorequire: 
         | 
| 10 10 | 
             
            bindir: bin
         | 
| 11 11 | 
             
            cert_chain: []
         | 
| 12 | 
            -
            date: 2017-03- | 
| 12 | 
            +
            date: 2017-03-23 00:00:00.000000000 Z
         | 
| 13 13 | 
             
            dependencies:
         | 
| 14 14 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 15 15 | 
             
              name: sinatra
         | 
| @@ -165,6 +165,20 @@ dependencies: | |
| 165 165 | 
             
                - - "~>"
         | 
| 166 166 | 
             
                  - !ruby/object:Gem::Version
         | 
| 167 167 | 
             
                    version: '1.3'
         | 
| 168 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 169 | 
            +
              name: commonmarker
         | 
| 170 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 171 | 
            +
                requirements:
         | 
| 172 | 
            +
                - - "<="
         | 
| 173 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 174 | 
            +
                    version: 0.14.4
         | 
| 175 | 
            +
              type: :runtime
         | 
| 176 | 
            +
              prerelease: false
         | 
| 177 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 178 | 
            +
                requirements:
         | 
| 179 | 
            +
                - - "<="
         | 
| 180 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 181 | 
            +
                    version: 0.14.4
         | 
| 168 182 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 169 183 | 
             
              name: mg
         | 
| 170 184 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -212,7 +226,6 @@ files: | |
| 212 226 | 
             
            - views/index.erb
         | 
| 213 227 | 
             
            - views/onepage.erb
         | 
| 214 228 | 
             
            - views/presenter.erb
         | 
| 215 | 
            -
            - views/stats copy.erb
         | 
| 216 229 | 
             
            - views/stats.erb
         | 
| 217 230 | 
             
            - public/css/TimeCircles-89ac5ae.css
         | 
| 218 231 | 
             
            - public/css/disconnected-large.png
         | 
| @@ -328,10 +341,15 @@ files: | |
| 328 341 | 
             
            - public/css/highlight/vs.css
         | 
| 329 342 | 
             
            - public/css/highlight/xcode.css
         | 
| 330 343 | 
             
            - public/css/highlight/zenburn.css
         | 
| 344 | 
            +
            - public/css/images/ui-icons_444444_256x240.png
         | 
| 345 | 
            +
            - public/css/images/ui-icons_555555_256x240.png
         | 
| 346 | 
            +
            - public/css/images/ui-icons_777620_256x240.png
         | 
| 347 | 
            +
            - public/css/images/ui-icons_777777_256x240.png
         | 
| 348 | 
            +
            - public/css/images/ui-icons_cc0000_256x240.png
         | 
| 349 | 
            +
            - public/css/images/ui-icons_ffffff_256x240.png
         | 
| 331 350 | 
             
            - public/css/jquery-ui-1.12.1.css
         | 
| 332 351 | 
             
            - public/css/mermaid-6.0.0.css
         | 
| 333 352 | 
             
            - public/css/onepage.css
         | 
| 334 | 
            -
            - public/css/pace.png
         | 
| 335 353 | 
             
            - public/css/paceMarker.png
         | 
| 336 354 | 
             
            - public/css/presenter.css
         | 
| 337 355 | 
             
            - public/css/showoff.css
         | 
    
        data/public/css/pace.png
    DELETED
    
    | Binary file | 
    
        data/views/stats copy.erb	
    DELETED
    
    | @@ -1,70 +0,0 @@ | |
| 1 | 
            -
            <!DOCTYPE html>
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
         | 
| 4 | 
            -
            <head>
         | 
| 5 | 
            -
                <%= erb :header_mini %>
         | 
| 6 | 
            -
             | 
| 7 | 
            -
                <script type="text/javascript">
         | 
| 8 | 
            -
                    $(document).ready(function(){ setupStats(); });
         | 
| 9 | 
            -
                </script>
         | 
| 10 | 
            -
            </head>
         | 
| 11 | 
            -
             | 
| 12 | 
            -
            <body id="stats">
         | 
| 13 | 
            -
              <div id="wrapper">
         | 
| 14 | 
            -
                <h1>Viewing Statistics</h1>
         | 
| 15 | 
            -
             | 
| 16 | 
            -
                <div id="least">
         | 
| 17 | 
            -
                  <h3>Least viewed slides</h3>
         | 
| 18 | 
            -
                  <% if @least.size > 0 %>
         | 
| 19 | 
            -
                    <% max = @least.sort_by {|slide, time| -time}[0][1].to_f %>
         | 
| 20 | 
            -
                    <% timestr =  (max > 3599) ?  '%H:%M:%S' : '%M:%S' %>
         | 
| 21 | 
            -
                    <% @least.each do |slide, time| %>
         | 
| 22 | 
            -
                      <div class="row">
         | 
| 23 | 
            -
                        <span class="label"><%= slide %></span>
         | 
| 24 | 
            -
                        <div class="bar" style="width: <%= (time/max)*100 %>%;"> </div>
         | 
| 25 | 
            -
                        <div class="time"><%= Time.at(time).gmtime.strftime(timestr) %></div>
         | 
| 26 | 
            -
                      </div>
         | 
| 27 | 
            -
                    <% end %>
         | 
| 28 | 
            -
                  <% end %>
         | 
| 29 | 
            -
                </div>
         | 
| 30 | 
            -
             | 
| 31 | 
            -
                <div id="most">
         | 
| 32 | 
            -
                  <h3>Most viewed slides</h3>
         | 
| 33 | 
            -
                  <% if @least.size > 0 %>
         | 
| 34 | 
            -
                    <% max = @most[0][1].to_f %>
         | 
| 35 | 
            -
                    <% timestr =  (max > 3599) ?  '%H:%M:%S' : '%M:%S' %>
         | 
| 36 | 
            -
                    <% @most.each do |slide, time| %>
         | 
| 37 | 
            -
                      <div class="row">
         | 
| 38 | 
            -
                        <span class="label"><%= slide %></span>
         | 
| 39 | 
            -
                        <div class="bar" style="width: <%= (time/max)*100 %>%;"> </div>
         | 
| 40 | 
            -
                        <div class="time"><%= Time.at(time).gmtime.strftime(timestr) %></div>
         | 
| 41 | 
            -
                      </div>
         | 
| 42 | 
            -
                    <% end %>
         | 
| 43 | 
            -
                  <% end %>
         | 
| 44 | 
            -
                </div>
         | 
| 45 | 
            -
             | 
| 46 | 
            -
                <div id="all">
         | 
| 47 | 
            -
                  <h3>All slides</h3>
         | 
| 48 | 
            -
                  <%# We reuse the max value calculated from the above step. %>
         | 
| 49 | 
            -
                  <% @all.sort.map do |slide, time| %>
         | 
| 50 | 
            -
                      <div class="row">
         | 
| 51 | 
            -
                      <span class="label"><%= slide %></span>
         | 
| 52 | 
            -
                      <div class="bar" style="width: <%= (time/max)*100 %>%;"> </div>
         | 
| 53 | 
            -
                      <div class="time"><%= Time.at(time).gmtime.strftime(timestr) %></div>
         | 
| 54 | 
            -
                      <% if @counter %>
         | 
| 55 | 
            -
                        <div class="detail">
         | 
| 56 | 
            -
                          <% @counter[slide].each do |host, count| %>
         | 
| 57 | 
            -
                            <div class="row">
         | 
| 58 | 
            -
                              <span class="label"><%= host %>:</span>
         | 
| 59 | 
            -
                              <div class="bar" style="width: <%= (count/max)*100 %>%;"> </div>
         | 
| 60 | 
            -
                              <div class="time"><%= Time.at(count).gmtime.strftime(timestr) %></div>
         | 
| 61 | 
            -
                            </div>
         | 
| 62 | 
            -
                          <% end %>
         | 
| 63 | 
            -
                        </div>
         | 
| 64 | 
            -
                      <% end %>
         | 
| 65 | 
            -
                    </div>
         | 
| 66 | 
            -
                  <% end %>
         | 
| 67 | 
            -
                </div>
         | 
| 68 | 
            -
              </div>
         | 
| 69 | 
            -
            </body>
         | 
| 70 | 
            -
            </html>
         |