<commit_before>extends layout
  
append externalScripts
  script(src='/scripts/noVNC/util.js')
  script(src='/scripts/vnc.js')
  
block content
  //- Enables fully-hidden table rows
  style(type='text/css')
    | .accordianRow {
    |     padding: 0 !important;
    | }

  // Full width panel is the main container for the dashboard
  div.container-fluid: div.panel.panel-default
    div.panel-heading: h3.panel-title Clasp Dashboard   
      span.small.pull-right#runxacrossy: // Will be filled in by t-runxacrossy
    div.panel-body
      // Embedded tabbed interface
      ul.nav.nav-tabs(role="tablist")#navtabs
        li.active: a(href="#summary",role="tab",data-toggle="tab") Summary
        li: a(href="#nodes",role="tab",data-toggle="tab") Nodes
        li: a(href="#emulators",role="tab",data-toggle="tab") Emulators
        li.dropdown
          a.dropdown-toggle(data-toggle="dropdown") System
            span.caret
          ul.dropdown-menu(role="menu")
            li: a(href="#metrics",role="tab",data-toggle="tab") Metrics
            li: a(href="#settings",role="tab",data-toggle="tab") Settings
            li: a(href="#refresh",role="tab",data-toggle="tab") Force Refresh
            li: a(href="#shutdownModal",data-toggle="modal",data-keyboard="true") Shutdown
        //- This empty tab is important - otherwise the last li inside #navtabs is 
        //- the one buried inside the dropdown, and device panels will be appended inside
        //- the dropdown
        li.disabled 
        //- Control panels added here by t-devicetab

      // Tab Panes
      div.tab-content#navcontent
        div.tab-pane.active#summary
          // Emulator Summary Next
          //- Panel helps to visually separate node summary and emulator summary
          div.panel.panel-default
            div.panel-heading: h3.panel-title Recently Updated Devices  
              span.small Click any row to get more information
            
            table.table.table-condensed.table-hover#devicesummary 
              //- Node rows will come from t-summarydevicerow
              tr
                th.col-md-1 VNC
                th.col-md-2 Worker Node
                th.col-md-2 Update
                th.col-md-2 Update Time
                th.col-md-5 UUID

          // Node Summary
          //- Panel helps to visually separate node summary and emulator summary
          div.panel.panel-default
            div.panel-heading: h3.panel-title Recently Updated Workers   
              span.small Click any row to get more information
            table.table.table-condensed.table-hover#nodesummary
              //- Node rows will come from t-summarynoderow
              tr
                th.col-md-1 
                th.col-md-2 IP
                th.col-md-2 Status
                th.col-md-2 Last Update
                th.col-md-5 UUID
        div.tab-pane#nodes: p Node stuff
        div.tab-pane#emulators: p Emulator stuff
        div.tab-pane#settings: p settings stuff
        div.tab-pane#metrics: p metrics stuff
        //- Control panes added here by t-devicetabpane

//- -------------------------------------------------------
//- 
//-      Modals for dashboard
//- 
//- -------------------------------------------------------

append modals
  div#shutdownModal.modal.fade(tabindex='-1')
    div.modal-dialog: div.modal-content
      div.modal-header
        button.close(type="button",data-dismiss="modal") &times
        h4.modal-title Are You Sure?
      div.modal-body
        p.text-danger This will shut down all devices, all worker nodes, and this website
      div.modal-footer
        button.btn.btn-success(data-dismiss="modal") Get Me Out of Here!
        button.btn.btn-danger(data-dismiss="modal",onclick="shutdown()") Shutdown

//- -------------------------------------------------------
//- 
//-      Useful javascript for dashboard
//- 
//- -------------------------------------------------------

append scriptOnReady
  | clasp.dash.change_tab = function(tabid) {
  |   // A bit pointless at the moment, but we may eventually 
  |   // want to modify the open websocket connections based on
  |   // the users current tab
  |   $('#navtabs a[href="' + tabid + '"]').tab('show');
  | };
  | 
  | // Track meta key to support cmd+click
  | clasp.metaPressed = false;
  | clasp._metaKeyCode = -1;
  | $(document).keydown(function(e) {
  |   if (e.metaKey || e.ctrlKey) {
  |     clasp.metaPressed = true;
  |     clasp._metaKeyCode = e.keyCode;
  |   }
  | });
  | $(document).keyup(function(e) {
  |   if (clasp.metaPressed && e.keyCode == clasp._metaKeyCode) {
  |     clasp.metaPressed = false;
  |   }
  | });
  | 
  | // From http://stackoverflow.com/a/11054415/119592
  | // Extend jquery with flashing for elements
  | $.fn.flash = function(duration, iterations) {
  |     console.log('Flashing');
  |     duration = duration || 1000; 
  |     iterations = iterations || 1; 
  |     var iterationDuration = Math.floor(duration / iterations);
  | 
  |     for (var i = 0; i < iterations; i++) {
  |         this.fadeOut(iterationDuration).fadeIn(iterationDuration);
  |     }
  |     return this;
  | }


//- -------------------------------------------------------
//- 
//-      Template definitions 
//- 
//- -------------------------------------------------------

//- Top right - Running 5 devices across 10 nodes
append templates
  script(id='t-runxacrossy',type='text/x-handlebars-template')
    | Running {{ emulators }} devices across {{ nodes }} nodes
append compileTemplates
  | templates.runxacrossy = Handlebars.compile($('#t-runxacrossy').html());
append scriptOnReady
  if mode == "dev"
    | setInterval(function() {
    |   var target = $('#runxacrossy');
    |   var context = {emulators: Math.floor((Math.random() * 20) + 1), nodes: "12"};
    |   var html = templates.runxacrossy(context);
    |   target.html(html);
    | }, 2000);


//- Summary node row
append templates
  script(id='t-summarynoderow',type='text/x-handlebars-template')
    tr.accordion-toggle(data-toggle="collapse",data-target="#ndetails-{{ uuid }}")
      td: button.btn.btn-xs.btn-info(onclick='clasp.dash.add_node_tab("{{ uuid }}")')
        span.glyphicon.glyphicon-cloud
      td {{ ip }}
      td(class="{{ update }}") {{ update }}
      td {{ updateTime }}
      td {{ uuid }}
    tr
      td.accordianRow(colspan="5")
        div.accordion-body.collapse(id="ndetails-{{ uuid }}") Details for Node {{ uuid }}
append compileTemplates
  | templates.summarynoderow = Handlebars.compile($('#t-summarynoderow').html());
append scriptOnReady
  | clasp.dash.summary.add_node = function(context) {
  |   var target = $('#nodesummary tr:last');
  |   var html = templates.summarynoderow(context);
  |   target.after(html);
  | };
  if mode == "dev"
    | var nsummary1 = {ip: "10.0.0.2", update: "Booting", 
    |                  updateTime: "10 seconds ago", uuid: "eec0d2cd-ef51-430f-bfc6-fec7e61e8bf6"};
    | var nsummary2 = {ip: "10.0.20.3", update: "Failed", 
    |                  updateTime: "1 minute ago", uuid: "77a3bc41-0d02-40a4-b68d-339d6b9a0124"};
    | var nsummary3 = {ip: "127.0.0.1", update: "Online", 
    |                  updateTime: "3 seconds ago", uuid: "62577119-8421-405f-9741-3f8e795ecc83"};
    | var nsummary4 = {ip: "10.0.20.7", update: "Offline", 
    |                  updateTime: "23 seconds ago", uuid: "7e0c0daf-b4c9-4330-9335-71ed38311155"};
    | clasp.dash.summary.add_node(nsummary1);
    | clasp.dash.summary.add_node(nsummary2);
    | clasp.dash.summary.add_node(nsummary3);
    | clasp.dash.summary.add_node(nsummary4);

//- Summary device row
append templates
  script(id='t-summarydevicerow',type='text/x-handlebars-template')
    tr.accordion-toggle(data-toggle="collapse",data-target="#dev-details-{{ uuid }}")
      td: button.btn.btn-xs.btn-primary(onclick='clasp.dash.add_device_tab("{{ uuid }}")')
        span.glyphicon.glyphicon-phone
      td {{ ip }}
      td {{ update }}
      td {{ updateTime }}
      td {{ uuid }}
    tr
      td.accordianRow(colspan="5")
        div.accordion-body.collapse(id="dev-details-{{ uuid }}") Details for Device {{ uuid }}
append compileTemplates
  | templates.summarydevicerow = Handlebars.compile($('#t-summarydevicerow').html());
append scriptOnReady
  | clasp.dash.summary.add_device = function(context) {
  |   var target = $('#devicesummary tr:last');
  |   var html = templates.summarydevicerow(context);
  |   target.after(html);
  | };
  if mode == "dev"
    | var dsummary1 = {ip: "10.0.0.2", update: "Crashed", 
    |                  updateTime: "10 seconds ago", uuid: "39513296-ef79-4311-8e92-3e6fcf13597c"};
    | var dsummary2 = {ip: "10.0.20.3", update: "Task Failed", 
    |                  updateTime: "1 minute ago", uuid: "591aeb1c-fd43-439e-aafa-0756d0cfb593"};
    | var dsummary3 = {ip: "127.0.0.1", update: "Task Success", 
    |                  updateTime: "22 seconds ago", uuid: "51459573-c743-4a82-a9a3-86eec3e9783e"};
    | var dsummary4 = {ip: "10.0.20.7", update: "Waiting For Task", 
    |                  updateTime: "23 seconds ago", uuid: "82ae6ee8-196e-4b50-af8b-92cb3355a1e2"};
    | clasp.dash.summary.add_device(dsummary1);
    | clasp.dash.summary.add_device(dsummary2);
    | clasp.dash.summary.add_device(dsummary3);
    | clasp.dash.summary.add_device(dsummary4);
    

//- Adds new device tab button and page
append templates
  script(id='t-devicetab',type='text/x-handlebars-template')
    li: a.btn.btn-primary(href="#devicetab-{{ uuid }}",role="tab",data-toggle="tab")
      span.glyphicon.glyphicon-phone 
  script(id='t-devicetabpane',type='text/x-handlebars-template')
    div.tab-pane(id="devicetab-{{ uuid }}")
      p Welcome to device {{ uuid }}
      div.row(style="margin-top: 20px")
        // TODO Hide side margins on small screens
        div.col-sm-5(style="margin-left: 10px; margin-right: 10px")
          canvas#noVNC_canvas1
        div.col-sm-1: div.btn-group-vertical
          button.btn.btn-default: span.glyphicon.glyphicon-home
          button.btn.btn-default: span.glyphicon.glyphicon-circle-arrow-left
          button.btn.btn-default: span.glyphicon.glyphicon-volume-down
          button.btn.btn-default: span.glyphicon.glyphicon-volume-up
          button.btn.btn-default: span.glyphicon.glyphicon-off
          button.btn.btn-default: span.glyphicon.glyphicon-zoom-out
          button.btn.btn-default: span.glyphicon.glyphicon-zoom-in
      div.row(style="margin-top: 20px")
        div.col-sm-12.pre-scrollable.well
          | This is where the emulator logs can be output  
append compileTemplates
  | templates.devicetab = Handlebars.compile($('#t-devicetab').html());
  | templates.devicetabpane = Handlebars.compile($('#t-devicetabpane').html());
append scriptOnReady
  | clasp.dash.add_device_tab = function(dev_uuid) {
  |   // Ensure we don't add twice
  |   if ($('#devicetab-'+dev_uuid).length != 0) {
  |     console.log('Refusing to create second tab for device ' + dev_uuid);
  |     $('#navtabs a[href="#devicetab-' + dev_uuid + '"]').flash(120,2)
  |     return;
  |   }
  | 
  |   // Add new tab button
  |   var lastTab = $('#navtabs li:last');
  |   lastTab.after(templates.devicetab({uuid: dev_uuid}));
  |   
  |   // Add new tab content
  |   var target = $('#navcontent div.tab-pane:last');
  |   var html = templates.devicetabpane({uuid: dev_uuid});
  |   target.after(html);
  |   
  |   if (!clasp.metaPressed)
  |     clasp.dash.change_tab('#devicetab-'+dev_uuid);
  | };


//- Adds new node tab button and page
append templates
  script(id='t-nodetab',type='text/x-handlebars-template')
    li: a.btn.btn-info(href="#nodetab-{{ uuid }}",role="tab",data-toggle="tab")
      span.glyphicon.glyphicon-cloud 
  script(id='t-nodetabpane',type='text/x-handlebars-template')
    div.tab-pane(id="nodetab-{{ uuid }}")
      p Welcome to node {{ uuid }}
append compileTemplates
  | templates.nodetab = Handlebars.compile($('#t-nodetab').html());
  | templates.nodetabpane = Handlebars.compile($('#t-nodetabpane').html());
append scriptOnReady
  | clasp.dash.add_node_tab = function(node_uuid) {
  |   // Ensure we don't add twice
  |   if ($('#nodetab-'+node_uuid).length != 0) {
  |     console.log('Refusing to create second tab for node ' + node_uuid);
  |     $('#navtabs a[href="#nodetab-' + node_uuid + '"]').flash(120,2)
  |     return;
  |   }
  | 
  |   // Add new tab button<commit_msg>Initiate VNC connection when device control tab is opened<commit_after>extends layout
  
append externalScripts
  script(src='/scripts/noVNC/util.js')
  script(src='/scripts/vnc.js')
  
block content
  //- Enables fully-hidden table rows
  style(type='text/css')
    | .accordianRow {
    |     padding: 0 !important;
    | }

  // Full width panel is the main container for the dashboard
  div.container-fluid: div.panel.panel-default
    div.panel-heading: h3.panel-title Clasp Dashboard   
      span.small.pull-right#runxacrossy: // Will be filled in by t-runxacrossy
    div.panel-body
      // Embedded tabbed interface
      ul.nav.nav-tabs(role="tablist")#navtabs
        li.active: a(href="#summary",role="tab",data-toggle="tab") Summary
        li: a(href="#nodes",role="tab",data-toggle="tab") Nodes
        li: a(href="#emulators",role="tab",data-toggle="tab") Emulators
        li.dropdown
          a.dropdown-toggle(data-toggle="dropdown") System
            span.caret
          ul.dropdown-menu(role="menu")
            li: a(href="#metrics",role="tab",data-toggle="tab") Metrics
            li: a(href="#settings",role="tab",data-toggle="tab") Settings
            li: a(href="#refresh",role="tab",data-toggle="tab") Force Refresh
            li: a(href="#shutdownModal",data-toggle="modal",data-keyboard="true") Shutdown
        //- This empty tab is important - otherwise the last li inside #navtabs is 
        //- the one buried inside the dropdown, and device panels will be appended inside
        //- the dropdown
        li.disabled 
        //- Control panels added here by t-devicetab

      // Tab Panes
      div.tab-content#navcontent
        div.tab-pane.active#summary
          // Emulator Summary Next
          //- Panel helps to visually separate node summary and emulator summary
          div.panel.panel-default
            div.panel-heading: h3.panel-title Recently Updated Devices  
              span.small Click any row to get more information
            
            table.table.table-condensed.table-hover#devicesummary 
              //- Node rows will come from t-summarydevicerow
              tr
                th.col-md-1 VNC
                th.col-md-2 Worker Node
                th.col-md-2 Update
                th.col-md-2 Update Time
                th.col-md-5 UUID

          // Node Summary
          //- Panel helps to visually separate node summary and emulator summary
          div.panel.panel-default
            div.panel-heading: h3.panel-title Recently Updated Workers   
              span.small Click any row to get more information
            table.table.table-condensed.table-hover#nodesummary
              //- Node rows will come from t-summarynoderow
              tr
                th.col-md-1 
                th.col-md-2 IP
                th.col-md-2 Status
                th.col-md-2 Last Update
                th.col-md-5 UUID
        div.tab-pane#nodes: p Node stuff
        div.tab-pane#emulators: p Emulator stuff
        div.tab-pane#settings: p settings stuff
        div.tab-pane#metrics: p metrics stuff
        //- Control panes added here by t-devicetabpane

//- -------------------------------------------------------
//- 
//-      Modals for dashboard
//- 
//- -------------------------------------------------------

append modals
  div#shutdownModal.modal.fade(tabindex='-1')
    div.modal-dialog: div.modal-content
      div.modal-header
        button.close(type="button",data-dismiss="modal") &times
        h4.modal-title Are You Sure?
      div.modal-body
        p.text-danger This will shut down all devices, all worker nodes, and this website
      div.modal-footer
        button.btn.btn-success(data-dismiss="modal") Get Me Out of Here!
        button.btn.btn-danger(data-dismiss="modal",onclick="shutdown()") Shutdown

//- -------------------------------------------------------
//- 
//-      Useful javascript for dashboard
//- 
//- -------------------------------------------------------

append scriptOnReady
  | clasp.dash.change_tab = function(tabid) {
  |   // A bit pointless at the moment, but we may eventually 
  |   // want to modify the open websocket connections based on
  |   // the users current tab
  |   $('#navtabs a[href="' + tabid + '"]').tab('show');
  | };
  | 
  | // Track meta key to support cmd+click
  | clasp.metaPressed = false;
  | clasp._metaKeyCode = -1;
  | $(document).keydown(function(e) {
  |   if (e.metaKey || e.ctrlKey) {
  |     clasp.metaPressed = true;
  |     clasp._metaKeyCode = e.keyCode;
  |   }
  | });
  | $(document).keyup(function(e) {
  |   if (clasp.metaPressed && e.keyCode == clasp._metaKeyCode) {
  |     clasp.metaPressed = false;
  |   }
  | });
  | 
  | // From http://stackoverflow.com/a/11054415/119592
  | // Extend jquery with flashing for elements
  | $.fn.flash = function(duration, iterations) {
  |     console.log('Flashing');
  |     duration = duration || 1000; 
  |     iterations = iterations || 1; 
  |     var iterationDuration = Math.floor(duration / iterations);
  | 
  |     for (var i = 0; i < iterations; i++) {
  |         this.fadeOut(iterationDuration).fadeIn(iterationDuration);
  |     }
  |     return this;
  | }


//- -------------------------------------------------------
//- 
//-      Template definitions 
//- 
//- -------------------------------------------------------

//- Top right - Running 5 devices across 10 nodes
append templates
  script(id='t-runxacrossy',type='text/x-handlebars-template')
    | Running {{ emulators }} devices across {{ nodes }} nodes
append compileTemplates
  | templates.runxacrossy = Handlebars.compile($('#t-runxacrossy').html());
append scriptOnReady
  if mode == "dev"
    | setInterval(function() {
    |   var target = $('#runxacrossy');
    |   var context = {emulators: Math.floor((Math.random() * 20) + 1), nodes: "12"};
    |   var html = templates.runxacrossy(context);
    |   target.html(html);
    | }, 2000);


//- Summary node row
append templates
  script(id='t-summarynoderow',type='text/x-handlebars-template')
    tr.accordion-toggle(data-toggle="collapse",data-target="#ndetails-{{ uuid }}")
      td: button.btn.btn-xs.btn-info(onclick='clasp.dash.add_node_tab("{{ uuid }}")')
        span.glyphicon.glyphicon-cloud
      td {{ ip }}
      td(class="{{ update }}") {{ update }}
      td {{ updateTime }}
      td {{ uuid }}
    tr
      td.accordianRow(colspan="5")
        div.accordion-body.collapse(id="ndetails-{{ uuid }}") Details for Node {{ uuid }}
append compileTemplates
  | templates.summarynoderow = Handlebars.compile($('#t-summarynoderow').html());
append scriptOnReady
  | clasp.dash.summary.add_node = function(context) {
  |   var target = $('#nodesummary tr:last');
  |   var html = templates.summarynoderow(context);
  |   target.after(html);
  | };
  if mode == "dev"
    | var nsummary1 = {ip: "10.0.0.2", update: "Booting", 
    |                  updateTime: "10 seconds ago", uuid: "eec0d2cd-ef51-430f-bfc6-fec7e61e8bf6"};
    | var nsummary2 = {ip: "10.0.20.3", update: "Failed", 
    |                  updateTime: "1 minute ago", uuid: "77a3bc41-0d02-40a4-b68d-339d6b9a0124"};
    | var nsummary3 = {ip: "127.0.0.1", update: "Online", 
    |                  updateTime: "3 seconds ago", uuid: "62577119-8421-405f-9741-3f8e795ecc83"};
    | var nsummary4 = {ip: "10.0.20.7", update: "Offline", 
    |                  updateTime: "23 seconds ago", uuid: "7e0c0daf-b4c9-4330-9335-71ed38311155"};
    | clasp.dash.summary.add_node(nsummary1);
    | clasp.dash.summary.add_node(nsummary2);
    | clasp.dash.summary.add_node(nsummary3);
    | clasp.dash.summary.add_node(nsummary4);

//- Summary device row
append templates
  script(id='t-summarydevicerow',type='text/x-handlebars-template')
    tr.accordion-toggle(data-toggle="collapse",data-target="#dev-details-{{ uuid }}")
      td: button.btn.btn-xs.btn-primary(onclick='clasp.dash.add_device_tab({uuid: "{{ uuid }}"})')
        span.glyphicon.glyphicon-phone
      td {{ ip }}
      td {{ update }}
      td {{ updateTime }}
      td {{ uuid }}
    tr
      td.accordianRow(colspan="5")
        div.accordion-body.collapse(id="dev-details-{{ uuid }}") Details for Device {{ uuid }}
append compileTemplates
  | templates.summarydevicerow = Handlebars.compile($('#t-summarydevicerow').html());
append scriptOnReady
  | clasp.dash.summary.add_device = function(context) {
  |   var target = $('#devicesummary tr:last');
  |   var html = templates.summarydevicerow(context);
  |   target.after(html);
  | };
  if mode == "dev"
    | var dsummary1 = {ip: "10.0.0.2", update: "Crashed", 
    |                  updateTime: "10 seconds ago", uuid: "39513296-ef79-4311-8e92-3e6fcf13597c"};
    | var dsummary2 = {ip: "10.0.20.3", update: "Task Failed", 
    |                  updateTime: "1 minute ago", uuid: "591aeb1c-fd43-439e-aafa-0756d0cfb593"};
    | var dsummary3 = {ip: "127.0.0.1", update: "Task Success", 
    |                  updateTime: "22 seconds ago", uuid: "51459573-c743-4a82-a9a3-86eec3e9783e"};
    | var dsummary4 = {ip: "10.0.20.7", update: "Waiting For Task", 
    |                  updateTime: "23 seconds ago", uuid: "82ae6ee8-196e-4b50-af8b-92cb3355a1e2"};
    | clasp.dash.summary.add_device(dsummary1);
    | clasp.dash.summary.add_device(dsummary2);
    | clasp.dash.summary.add_device(dsummary3);
    | clasp.dash.summary.add_device(dsummary4);
    

//- Adds new device tab button and page
append templates
  script(id='t-devicetab',type='text/x-handlebars-template')
    li: a.btn.btn-primary(href="#devicetab-{{ uuid }}",role="tab",data-toggle="tab")
      span.glyphicon.glyphicon-phone 
  script(id='t-devicetabpane',type='text/x-handlebars-template')
    div.tab-pane(id="devicetab-{{ uuid }}")
      p Welcome to device {{ uuid }}
      div.row(style="margin-top: 20px")
        // TODO Hide side margins on small screens
        div.col-sm-5(style="margin-left: 10px; margin-right: 10px")
          canvas(id="devicecanvas-{{ uuid }}")
        div.col-sm-1: div.btn-group
          button.btn.btn-default: span.glyphicon.glyphicon-home
          button.btn.btn-default: span.glyphicon.glyphicon-circle-arrow-left
          button.btn.btn-default: span.glyphicon.glyphicon-volume-down
          button.btn.btn-default: span.glyphicon.glyphicon-volume-up
          button.btn.btn-default: span.glyphicon.glyphicon-off
          button.btn.btn-default: span.glyphicon.glyphicon-zoom-out
          button.btn.btn-default: span.glyphicon.glyphicon-zoom-in
      div.row(style="margin-top: 20px")
        div.col-sm-12.pre-scrollable.well
          | This is where the emulator logs can be output  
append compileTemplates
  | templates.devicetab = Handlebars.compile($('#t-devicetab').html());
  | templates.devicetabpane = Handlebars.compile($('#t-devicetabpane').html());
append scriptOnReady
  | clasp.dash.add_device_tab = function(context) {
  |   if (context.uuid === undefined) context.uuid = 'none'; var uuid = context.uuid;
  |   if (context.host === undefined) context.host = 'localhost';
  |   if (context.port === undefined) context.port = '6080';
  |   console.log('Built context ' + context);
  | 
  |   // Ensure we don't add twice
  |   if ($('#devicetab-' + uuid).length != 0) {
  |     console.log('Refusing to create second tab for device ' + uuid);
  |     $('#navtabs a[href="#devicetab-' + uuid + '"]').flash(120,2)
  |     return;
  |   }
  | 
  |   // Add new tab button
  |   var lastTab = $('#navtabs li:last');
  |   lastTab.after(templates.devicetab({uuid: uuid}));
  |   
  |   // Add new tab content
  |   var target = $('#navcontent div.tab-pane:last');
  |   var html = templates.devicetabpane({uuid: uuid});
  |   target.after(html);
  | 
  |   // Create RFB for new tab
  |   var rfb = clasp.dash.vnc.setup(context.host, context.port, 'devicecanvas-' + uuid);
  |   clasp.dash.vnc[uuid] = rfb;
  |   
  |   if (!clasp.metaPressed)
  |     clasp.dash.change_tab('#devicetab-'+uuid);
  | };

//- Adds new node tab button and page
append templates
  script(id='t-nodetab',type='text/x-handlebars-template')
    li: a.btn.btn-info(href="#nodetab-{{ uuid }}",role="tab",data-toggle="tab")
      span.glyphicon.glyphicon-cloud 
  script(id='t-nodetabpane',type='text/x-handlebars-template')
    div.tab-pane(id="nodetab-{{ uuid }}")
      p Welcome to node {{ uuid }}
append compileTemplates
  | templates.nodetab = Handlebars.compile($('#t-nodetab').html());
  | templates.nodetabpane = Handlebars.compile($('#t-nodetabpane').html());
append scriptOnReady
  | clasp.dash.add_node_tab = function(node_uuid) {
  |   // Ensure we don't add twice
  |   if ($('#nodetab-'+node_uuid).length != 0) {
  |     console.log('Refusing to create second tab for node ' + node_uuid);
  |     $('#navtabs a[href="#nodetab-' + node_uuid + '"]').flash(120,2)
  |     return;
  |   }
  | 
  |   // Add new tab button
{'fraction_non_alphanumeric': 0.17392678014296434, 'fraction_numerical': 0.02507799849926938, 'mean_word_length': 2.3830327321309284, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 16, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '324397', 'n_tokens_mistral': 8708, 'n_tokens_neox': 8222, 'n_words': 1605}