let log = [
  {
    "action": "valueChange",
    "id": "names(input)",
    "value": " chr \"dataset\"",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212774.9168
  },
  {
    "action": "valueChange",
    "id": "input (all)",
    "value": "List of 1\n $ dataset: chr \"rock\"",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212774.919
  },
  {
    "action": "valueChange",
    "id": "input$dataset",
    "value": " chr \"rock\"",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212774.9207
  },
  {
    "action": "valueChange",
    "id": "names(input)",
    "value": " chr [1:2] \"caption\" \"dataset\"",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212774.9222
  },
  {
    "action": "valueChange",
    "id": "input (all)",
    "value": "List of 2\n $ caption: chr \"Data Summary\"\n $ dataset: chr \"rock\"",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212774.9243
  },
  {
    "action": "valueChange",
    "id": "input$caption",
    "value": " chr \"Data Summary\"",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212774.9283
  },
  {
    "action": "valueChange",
    "id": "names(input)",
    "value": " chr [1:3] \"caption\" \"dataset\" \"obs\"",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212774.9298
  },
  {
    "action": "valueChange",
    "id": "input (all)",
    "value": "List of 3\n $ caption: chr \"Data Summary\"\n $ obs    : int 10\n $ dataset: chr \"rock\"",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212774.9323
  },
  {
    "action": "valueChange",
    "id": "input$obs",
    "value": " int 10",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212774.9339
  },
  {
    "action": "valueChange",
    "id": "names(clientData)",
    "value": " chr \"output_caption_hidden\"",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212774.9355
  },
  {
    "action": "valueChange",
    "id": "clientData (all)",
    "value": "List of 1\n $ output_caption_hidden: logi FALSE",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212774.9373
  },
  {
    "action": "valueChange",
    "id": "clientData$output_caption_hidden",
    "value": " logi FALSE",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212774.9387
  },
  {
    "action": "valueChange",
    "id": "names(clientData)",
    "value": " chr [1:2] \"output_caption_hidden\" \"output_summary_hidden\"",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212774.9401
  },
  {
    "action": "valueChange",
    "id": "clientData (all)",
    "value": "List of 2\n $ output_caption_hidden: logi FALSE\n $ output_summary_hidden: logi FALSE",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212774.9424
  },
  {
    "action": "valueChange",
    "id": "clientData$output_summary_hidden",
    "value": " logi FALSE",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212774.944
  },
  {
    "action": "valueChange",
    "id": "names(clientData)",
    "value": " chr [1:3] \"output_caption_hidden\" \"output_summary_hidden\" ...",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212774.9455
  },
  {
    "action": "valueChange",
    "id": "clientData (all)",
    "value": "List of 3\n $ output_view_hidden   : logi FALSE\n $ output_caption_hidden: logi FALSE\n $ output_summary_hidden: logi FALSE",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212774.9481
  },
  {
    "action": "valueChange",
    "id": "clientData$output_view_hidden",
    "value": " logi FALSE",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212774.9494
  },
  {
    "action": "valueChange",
    "id": "names(clientData)",
    "value": " chr [1:4] \"output_caption_hidden\" \"output_summary_hidden\" ...",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212774.9509
  },
  {
    "action": "valueChange",
    "id": "clientData (all)",
    "value": "List of 4\n $ output_view_hidden   : logi FALSE\n $ output_caption_hidden: logi FALSE\n $ pixelratio           : int 2\n $ output_summary_hidden: logi FALSE",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212774.954
  },
  {
    "action": "valueChange",
    "id": "clientData$pixelratio",
    "value": " int 2",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212774.9555
  },
  {
    "action": "valueChange",
    "id": "names(clientData)",
    "value": " chr [1:5] \"output_caption_hidden\" \"output_summary_hidden\" ...",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212774.9569
  },
  {
    "action": "valueChange",
    "id": "clientData (all)",
    "value": "List of 5\n $ output_view_hidden   : logi FALSE\n $ output_caption_hidden: logi FALSE\n $ pixelratio           : int 2\n $ output_summary_hidden: logi FALSE\n $ url_protocol         : chr \"http:\"",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212774.9605
  },
  {
    "action": "valueChange",
    "id": "clientData$url_protocol",
    "value": " chr \"http:\"",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212774.9619
  },
  {
    "action": "valueChange",
    "id": "names(clientData)",
    "value": " chr [1:6] \"output_caption_hidden\" \"output_summary_hidden\" ...",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212774.9633
  },
  {
    "action": "valueChange",
    "id": "clientData (all)",
    "value": "List of 6\n $ output_view_hidden   : logi FALSE\n $ output_caption_hidden: logi FALSE\n $ pixelratio           : int 2\n $ url_hostname         : chr \"127.0.0.1\"\n $ output_summary_hidden: logi FALSE\n $ url_protocol         : chr \"http:\"",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212774.9671
  },
  {
    "action": "valueChange",
    "id": "clientData$url_hostname",
    "value": " chr \"127.0.0.1\"",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212774.9683
  },
  {
    "action": "valueChange",
    "id": "names(clientData)",
    "value": " chr [1:7] \"output_caption_hidden\" \"output_summary_hidden\" ...",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212774.9697
  },
  {
    "action": "valueChange",
    "id": "clientData (all)",
    "value": "List of 7\n $ output_view_hidden   : logi FALSE\n $ url_port             : chr \"5643\"\n $ output_caption_hidden: logi FALSE\n $ pixelratio           : int 2\n $ url_hostname         : chr \"127.0.0.1\"\n $ output_summary_hidden: logi FALSE\n $ url_protocol         : chr \"http:\"",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212774.9756
  },
  {
    "action": "valueChange",
    "id": "clientData$url_port",
    "value": " chr \"5643\"",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212774.9769
  },
  {
    "action": "valueChange",
    "id": "names(clientData)",
    "value": " chr [1:8] \"output_caption_hidden\" \"output_summary_hidden\" ...",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212774.9784
  },
  {
    "action": "valueChange",
    "id": "clientData (all)",
    "value": "List of 8\n $ url_pathname         : chr \"\/\"\n $ output_view_hidden   : logi FALSE\n $ url_port             : chr \"5643\"\n $ output_caption_hidden: logi FALSE\n $ pixelratio           : int 2\n $ url_hostname         : chr \"127.0.0.1\"\n $ output_summary_hidden: logi FALSE\n $ url_protocol         : chr \"http:\"",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212774.9828
  },
  {
    "action": "valueChange",
    "id": "clientData$url_pathname",
    "value": " chr \"\/\"",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212774.9841
  },
  {
    "action": "valueChange",
    "id": "names(clientData)",
    "value": " chr [1:9] \"output_caption_hidden\" \"output_summary_hidden\" ...",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212774.9856
  },
  {
    "action": "valueChange",
    "id": "clientData (all)",
    "value": "List of 9\n $ url_pathname         : chr \"\/\"\n $ output_view_hidden   : logi FALSE\n $ url_port             : chr \"5643\"\n $ output_caption_hidden: logi FALSE\n $ pixelratio           : int 2\n $ url_hostname         : chr \"127.0.0.1\"\n $ output_summary_hidden: logi FALSE\n $ url_search           : chr \"\"\n $ url_protocol         : chr \"http:\"",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212774.9904
  },
  {
    "action": "valueChange",
    "id": "clientData$url_search",
    "value": " chr \"\"",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212774.9918
  },
  {
    "action": "valueChange",
    "id": "names(clientData)",
    "value": " chr [1:10] \"output_caption_hidden\" \"output_summary_hidden\" ...",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212774.9934
  },
  {
    "action": "valueChange",
    "id": "clientData (all)",
    "value": "List of 10\n $ url_pathname         : chr \"\/\"\n $ output_view_hidden   : logi FALSE\n $ url_port             : chr \"5643\"\n $ output_caption_hidden: logi FALSE\n $ url_hash_initial     : chr \"\"\n $ pixelratio           : int 2\n $ url_hostname         : chr \"127.0.0.1\"\n $ output_summary_hidden: logi FALSE\n $ url_search           : chr \"\"\n $ url_protocol         : chr \"http:\"",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212774.9985
  },
  {
    "action": "valueChange",
    "id": "clientData$url_hash_initial",
    "value": " chr \"\"",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212774.9998
  },
  {
    "action": "valueChange",
    "id": "names(clientData)",
    "value": " chr [1:11] \"output_caption_hidden\" \"output_summary_hidden\" ...",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212775.0015
  },
  {
    "action": "valueChange",
    "id": "clientData (all)",
    "value": "List of 11\n $ url_pathname         : chr \"\/\"\n $ output_view_hidden   : logi FALSE\n $ url_port             : chr \"5643\"\n $ output_caption_hidden: logi FALSE\n $ url_hash_initial     : chr \"\"\n $ pixelratio           : int 2\n $ singletons           : chr \"\"\n $ url_hostname         : chr \"127.0.0.1\"\n $ output_summary_hidden: logi FALSE\n $ url_search           : chr \"\"\n $ url_protocol         : chr \"http:\"",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212775.0077
  },
  {
    "action": "valueChange",
    "id": "clientData$singletons",
    "value": " chr \"\"",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212775.0094
  },
  {
    "action": "valueChange",
    "id": "names(clientData)",
    "value": " chr [1:12] \"allowDataUriScheme\" \"output_caption_hidden\" ...",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212775.0113
  },
  {
    "action": "valueChange",
    "id": "clientData (all)",
    "value": "List of 12\n $ allowDataUriScheme   : logi TRUE\n $ url_pathname         : chr \"\/\"\n $ output_view_hidden   : logi FALSE\n $ url_port             : chr \"5643\"\n $ output_caption_hidden: logi FALSE\n $ url_hash_initial     : chr \"\"\n $ pixelratio           : int 2\n $ singletons           : chr \"\"\n $ url_hostname         : chr \"127.0.0.1\"\n $ output_summary_hidden: logi FALSE\n $ url_search           : chr \"\"\n $ url_protocol         : chr \"http:\"",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212775.018
  },
  {
    "action": "valueChange",
    "id": "clientData$allowDataUriScheme",
    "value": " logi TRUE",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212775.0194
  },
  {
    "action": "ctx",
    "id": "1",
    "label": "output$caption",
    "srcref": [
      34,
      32,
      35,
      17,
      32,
      17
    ],
    "srcfile": "server.R",
    "type": "observer",
    "prevId": "",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212775.0244
  },
  {
    "action": "invalidate",
    "id": "1",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212775.0259
  },
  {
    "action": "ctx",
    "id": "2",
    "label": "output$summary",
    "srcref": [
      42,
      33,
      44,
      20,
      33,
      20
    ],
    "srcfile": "server.R",
    "type": "observer",
    "prevId": "",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212775.0286
  },
  {
    "action": "invalidate",
    "id": "2",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212775.0297
  },
  {
    "action": "ctx",
    "id": "3",
    "label": "output$view",
    "srcref": [
      50,
      30,
      51,
      39,
      30,
      39
    ],
    "srcfile": "server.R",
    "type": "observer",
    "prevId": "",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212775.0319
  },
  {
    "action": "invalidate",
    "id": "3",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212775.0331
  },
  {
    "action": "ctx",
    "id": "4",
    "label": "output$caption",
    "srcref": [
      34,
      32,
      35,
      17,
      32,
      17
    ],
    "srcfile": "server.R",
    "type": "observer",
    "prevId": "1",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212775.0345
  },
  {
    "action": "enter",
    "id": "4",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212775.0357
  },
  {
    "action": "dep",
    "id": "4",
    "dependsOn": "input$caption",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212775.0373
  },
  {
    "action": "exit",
    "id": "4",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212775.0395
  },
  {
    "action": "ctx",
    "id": "5",
    "label": "output$summary",
    "srcref": [
      42,
      33,
      44,
      20,
      33,
      20
    ],
    "srcfile": "server.R",
    "type": "observer",
    "prevId": "2",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212775.0405
  },
  {
    "action": "enter",
    "id": "5",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212775.0416
  },
  {
    "action": "ctx",
    "id": "6",
    "label": "datasetInput",
    "srcref": [
      16,
      5,
      19,
      25,
      5,
      25,
      16,
      19
    ],
    "srcfile": "server.R",
    "type": "observable",
    "prevId": "",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212775.0436
  },
  {
    "action": "enter",
    "id": "6",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212775.0447
  },
  {
    "action": "dep",
    "id": "6",
    "dependsOn": "input$dataset",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212775.0455
  },
  {
    "action": "exit",
    "id": "6",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212775.0478
  },
  {
    "action": "depId",
    "id": "5",
    "dependsOn": "6",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212775.0487
  },
  {
    "action": "exit",
    "id": "5",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212775.0552
  },
  {
    "action": "ctx",
    "id": "7",
    "label": "output$view",
    "srcref": [
      50,
      30,
      51,
      39,
      30,
      39
    ],
    "srcfile": "server.R",
    "type": "observer",
    "prevId": "3",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212775.0562
  },
  {
    "action": "enter",
    "id": "7",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212775.0574
  },
  {
    "action": "depId",
    "id": "7",
    "dependsOn": "6",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212775.0589
  },
  {
    "action": "dep",
    "id": "7",
    "dependsOn": "input$obs",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212775.0598
  },
  {
    "action": "exit",
    "id": "7",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212775.069
  },
  {
    "action": "valueChange",
    "id": "names(input)",
    "value": " chr [1:3] \"caption\" \"dataset\" \"obs\"",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212777.7149
  },
  {
    "action": "valueChange",
    "id": "input (all)",
    "value": "List of 3\n $ caption: chr \"Data Summary 1\"\n $ obs    : int 10\n $ dataset: chr \"rock\"",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212777.7181
  },
  {
    "action": "valueChange",
    "id": "input$caption",
    "value": " chr \"Data Summary 1\"",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212777.7198
  },
  {
    "action": "invalidate",
    "id": "4",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212777.7208
  },
  {
    "action": "ctx",
    "id": "8",
    "label": "output$caption",
    "srcref": [
      34,
      32,
      35,
      17,
      32,
      17
    ],
    "srcfile": "server.R",
    "type": "observer",
    "prevId": "4",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212777.7236
  },
  {
    "action": "enter",
    "id": "8",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212777.7249
  },
  {
    "action": "dep",
    "id": "8",
    "dependsOn": "input$caption",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212777.7266
  },
  {
    "action": "exit",
    "id": "8",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212777.7284
  },
  {
    "action": "valueChange",
    "id": "names(input)",
    "value": " chr [1:3] \"caption\" \"dataset\" \"obs\"",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212779.4006
  },
  {
    "action": "valueChange",
    "id": "input (all)",
    "value": "List of 3\n $ caption: chr \"Data Summary 1\"\n $ obs    : int 10\n $ dataset: chr \"pressure\"",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212779.4035
  },
  {
    "action": "valueChange",
    "id": "input$dataset",
    "value": " chr \"pressure\"",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212779.4049
  },
  {
    "action": "invalidate",
    "id": "6",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212779.4058
  },
  {
    "action": "invalidate",
    "id": "5",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212779.4065
  },
  {
    "action": "invalidate",
    "id": "7",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212779.4089
  },
  {
    "action": "ctx",
    "id": "9",
    "label": "output$summary",
    "srcref": [
      42,
      33,
      44,
      20,
      33,
      20
    ],
    "srcfile": "server.R",
    "type": "observer",
    "prevId": "5",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212779.4109
  },
  {
    "action": "enter",
    "id": "9",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212779.412
  },
  {
    "action": "ctx",
    "id": "10",
    "label": "datasetInput",
    "srcref": [
      16,
      5,
      19,
      25,
      5,
      25,
      16,
      19
    ],
    "srcfile": "server.R",
    "type": "observable",
    "prevId": "6",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212779.4142
  },
  {
    "action": "enter",
    "id": "10",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212779.4153
  },
  {
    "action": "dep",
    "id": "10",
    "dependsOn": "input$dataset",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212779.4161
  },
  {
    "action": "exit",
    "id": "10",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212779.4169
  },
  {
    "action": "depId",
    "id": "9",
    "dependsOn": "10",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212779.4177
  },
  {
    "action": "exit",
    "id": "9",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212779.4777
  },
  {
    "action": "ctx",
    "id": "11",
    "label": "output$view",
    "srcref": [
      50,
      30,
      51,
      39,
      30,
      39
    ],
    "srcfile": "server.R",
    "type": "observer",
    "prevId": "7",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212779.4789
  },
  {
    "action": "enter",
    "id": "11",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212779.48
  },
  {
    "action": "depId",
    "id": "11",
    "dependsOn": "10",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212779.4818
  },
  {
    "action": "dep",
    "id": "11",
    "dependsOn": "input$obs",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212779.4827
  },
  {
    "action": "exit",
    "id": "11",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212779.4903
  },
  {
    "action": "valueChange",
    "id": "names(input)",
    "value": " chr [1:3] \"caption\" \"dataset\" \"obs\"",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212781.0019
  },
  {
    "action": "valueChange",
    "id": "input (all)",
    "value": "List of 3\n $ caption: chr \"Data Summary 1\"\n $ obs    : int 100\n $ dataset: chr \"pressure\"",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212781.0052
  },
  {
    "action": "valueChange",
    "id": "input$obs",
    "value": " int 100",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212781.0069
  },
  {
    "action": "invalidate",
    "id": "11",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212781.008
  },
  {
    "action": "ctx",
    "id": "12",
    "label": "output$view",
    "srcref": [
      50,
      30,
      51,
      39,
      30,
      39
    ],
    "srcfile": "server.R",
    "type": "observer",
    "prevId": "11",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212781.0119
  },
  {
    "action": "enter",
    "id": "12",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212781.0132
  },
  {
    "action": "depId",
    "id": "12",
    "dependsOn": "10",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212781.0155
  },
  {
    "action": "dep",
    "id": "12",
    "dependsOn": "input$obs",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212781.0172
  },
  {
    "action": "exit",
    "id": "12",
    "session": "36377b30e77d6f2f1f5fc5628b882ee5",
    "time": 1449212781.0257
  }
];
let time = true;
try {
  log = __DATA__;
  time = String(__TIME__).toLowerCase() === 'true';
} catch (e) {}
let nodes = {};
let nodeList = [];
let nodeSelection = null;
let links = [];
let linkSelection = null;
let node, link;  // d3 selections
let MAX_LINES = 6;
let force = d3.layout.force()
    .charge(-100)
    .nodes(nodeList)
    .links(links);
force.on('tick', onTick);
function pathDataForNode(node) {
  switch (node.type) {
    case 'observer':
      return 'M -25,-50  c -75,0 -75,100 0,100  l 100,0  l 0,-100 Z';
    case 'observable':
      return 'M -25,-50  c -75,0 -75,100 0,100  l 60,0  l 50,-50  l -50,-50  Z';
    case 'value':
      return 'M -50,-50 l 0,100 l 100,0  l 50,-50  l -50,-50  Z';
  }
}
function getSourceCoords(node) {
  switch (node.type) {
    case 'observer':
    case 'observable':
      return {x: node.x - 5, y: node.y};
    default:
      return {x: node.x, y: node.y};
  }
}
function getTargetCoords(node) {
  switch (node.type) {
    case 'observable':
      return {x: node.x + 7, y: node.y};
    case 'value':
      return {x: node.x + 8, y: node.y};
    default:
      return {x: node.x, y: node.y};
  }
}
function compare(a, b) {
  if (a.id < b.id)
    return -1;
  else if (a.id > b.id)
    return 1;
  else
    return 0;
}
let timeDifferences = function() {
  let enters = log.filter(function(obj) { return obj.action === 'enter'; });
  enters.sort(compare);
  let exits = log.filter(function(obj) { return obj.action === 'exit'; });
  exits.sort(compare);
  let diff = [];
  for (let i = 0; i < exits.length; i++)
    diff.push(exits[i].time - enters[i].time);
  return diff.map(function(x) { return x * 1000; });
}();
// colors taken from colorbrewer's 9-class Reds: http://colorbrewer2.org/?type=sequential&scheme=Reds&n=9
let colorScale = d3.scale.quantize()
                         .domain([d3.min(timeDifferences), d3.max(timeDifferences)])
                         .range(['#fcbba1','#fc9272','#fb6a4a','#ef3b2c','#cb181d','#a50f15']);
function update() {
  force.size([document.documentElement.clientWidth / 4,
              document.documentElement.clientHeight / 4]);
  let layoutDirty = false;
  node = d3.select('#nodes').selectAll('.node').data(nodeList);
  layoutDirty = layoutDirty || !node.enter().empty() || !node.exit().empty();
  let newG = node.enter().append('g')
      .attr('class', function(n) {return 'node ' + n.type;})
      .attr('r', 5)
      // don't show until next tick
      .style('display', 'none')
      .on('mousedown', function() {
        d3.event.stopPropagation();
      })
      .on('mouseover', function(n) {
        $('#description').text(n.label);
      })
      .on('mouseout', function(d, i) {
        $('#description').text('');
      })
      .on('dblclick', function(d) {
        d3.event.stopPropagation();
        d3.select(this).classed('fixed', d.fixed = false);
      })
      .call(force.drag().on('dragstart', function(d) {
        d3.select(this).classed('fixed', d.fixed = true);
      }));
  newG.append('path')
      .attr('transform', 'scale(0.08)')
      .attr('stroke', 'black')
      .attr('stroke-width', 4)
      .attr('fill', 'white')
      .attr('d', pathDataForNode);
  newG.append('text')
      .attr('x', 3)
      .attr('y', 1.5)
      .attr('font-size', 3.25)
      .attr('transform', function(n) {
        if (n.type !== 'observer')
          return 'translate(1.5, 0)';
        else
          return null;
      });
  node.exit().remove();
  node
      .classed('invalidated', function(n) { return n.invalidated; })
      .classed('running', function(n) { return n.running; })
      .classed('changed', function(n) { return n.changed; })
      .attr('fill', function(n) {
        if (n.invalidated)
          return "url(#diagonalHatch)";
        else
          return null;
      });
  let tspanLabel = node.selectAll('text').filter(function(n) {
    // This filter is used to disregard all nodes whose labels have
    // not changed since the last time we updated them.
    let changed = n.label !== this.label;
    this.label = n.label;
    return changed;
  }).selectAll('.tspanLabel')
    .data(function(n) {
      let lines = n.label.replace(/ /g, '\xA0').split('\n');
      if (lines.length > MAX_LINES) {
        lines.splice(MAX_LINES);
      }
      return lines;
    });
  tspanLabel.enter().append('tspan');
  tspanLabel.exit().remove();
  tspanLabel
      .attr('x', 8)
      .attr('dy', function(line, i) { return i > 0 ? '1em' : 0;})
      .attr('opacity', function(line, i) {
        return Math.min(1, (MAX_LINES - i) * 0.25 - 0.15);
      })
      .classed('tspanLabel', true)
      .text(function(line) { return line; });
  let tspanTime = node.selectAll('text').filter(function(n) {
    let changed = n.timeElapsed !== this.timeElapsed;
    this.timeElapsed = n.timeElapsed;
    return changed;
  }).selectAll('.tspanTime')
    .data(function(n) {
      return [n.timeElapsed];
    });
  tspanTime.enter().append('tspan');
  tspanTime.exit().remove();
  tspanTime
      .attr('x', 8)
      .attr('y', -2)
      .attr('fill', function(time) { return colorScale(time); })
      .classed('tspanTime', true)
      .text(function(time) {
        if (time === null) return '';
        else return 'time elapsed: ' + (Math.round(time * 1000) / 1000) + ' ms';
      });
  link = d3.select('#links').selectAll('.link').data(links);
  layoutDirty = layoutDirty || !link.enter().empty() || !link.exit().empty();
  link.enter().append('path')
      .attr('class', 'link')
      .attr('marker-mid', 'url(#triangle)');
  link.exit().remove();
  if (layoutDirty) {
    force
      .nodes(nodeList.filter(function(n) {return !n.hide;}))
      .start();
    layoutDirty = false;
  }
}
function onTick() {
  node
      .style('display', null)
      .attr('transform', function(n) {
        return 'translate(' + n.x + ' ' + n.y + ')';
      });
  link
      .attr('d', function(link) {
        let source = getSourceCoords(link.source);
        let target = getTargetCoords(link.target);
        let mid = {
          x: (source.x + target.x) / 2,
          y: (source.y + target.y) / 2
        };
        return 'M' + source.x + ',' + source.y +
          ' L' + mid.x + ',' + mid.y +
          ' L' + target.x + ',' + target.y;
      });
}
function createNodeWithUndo(data) {
  let node;
  if (!data.prevId) {
    node = {
      label: data.label,
      type: data.type,
      hide: data.hide
    };
    nodes[data.id] = node;
    pushUndo(function() {
      delete nodes[data.id];
    });
    if (!node.hide) {
      nodeList.push(node);
      pushUndo(function() {
        nodeList.pop();
      });
    }
  } else {
    node = nodes[data.prevId];
    let oldLabel = node.label;
    let oldInvalidated = node.invalidated;
    delete nodes[data.prevId];
    nodes[data.id] = node;
    node.label = data.label;
    node.invalidated = false;
    pushUndo(function() {
      node.label = oldLabel;
      node.invalidated = oldInvalidated;
      delete nodes[data.id];
      nodes[data.prevId] = node;
    });
  }
}
Array.prototype.pushWithUndo = function(value) {
  let self = this;
  this.push(value);
  pushUndo(function() {
    self.pop();
  });
};
Array.prototype.shiftWithUndo = function(value) {
  let self = this;
  var value = this.shift();
  pushUndo(function() {
    self.unshift(value);
  });
  return value;
};
let undoStack = [];
let currentUndos = null;
function startUndoScope() {
  if (currentUndos !== null)
    throw new Error('Illegal state');
  currentUndos = [];
}
function pushUndo(func) {
  currentUndos.push(func);
}
function endUndoScope() {
  let localUndos = currentUndos;
  undoStack.push(function() {
    while (localUndos.length) {
      localUndos.pop()();
    }
  });
  currentUndos = null;
}
function undo() {
  if (undoStack.length) {
    undoStack.pop()();
    update();
    return true;
  }
  return false;
}
function undoAll() {
  while (undo()) {}
}
// Here we monkeypatch Math.random to take part in the undo mechanism.
// This allows "random" d3 force-layout decisions to be reproducible.
// If we don't do this, then doing/undoing/redoing a node creation step
// looks very confusing, as the node comes flying in from a different
// direction each time.
let trueRandom = Math.random;
Math.random = (function() {
  let randomStack = [];
  return function() {
    if (!currentUndos)
      return trueRandom();
    let value;
    if (randomStack.length > 0) {
      value = randomStack.pop();
    }
    else {
      value = trueRandom();
    }
    pushUndo(function() {
      randomStack.push(value);
    });
    return value;
  };
})();
let callbacks = {
  ctx: function(data) {
    createNodeWithUndo(data);
    return true;
  },
  dep: function(data) {
    let dependsOn = nodes[data.dependsOn];
    if (!dependsOn) {
      createNodeWithUndo({
        id: data.dependsOn,
        label: data.dependsOn,
        type: 'value'
      });
      dependsOn = nodes[data.dependsOn];
    }
    if (dependsOn.hide) {
      dependsOn.hide = false;
      nodeList.push(dependsOn);
      pushUndo(function() {
        dependsOn.hide = true;
        nodeList.pop();
      });
    }
    links.push({
      source: nodes[data.id],
      target: nodes[data.dependsOn]
    });
    pushUndo(function() {
      links.pop();
    });
  },
  depId: function(data) {
    links.push({
      source: nodes[data.id],
      target: nodes[data.dependsOn]
    });
    pushUndo(function() {
      links.pop();
    });
  },
  invalidate: function(data) {
    let node = nodes[data.id];
    if (node.invalidated)
      throw new Error('Illegal sequence');
    node.invalidated = true;
    pushUndo(function() {
      node.invalidated = false;
    });
    let origLinks = links;
    links = links.filter(function(link) {
      return link.source !== node;
    });
    pushUndo(function() {
      links = origLinks;
    });
  },
  valueChange: function(data) {
    let existed = !!nodes[data.id];
    createNodeWithUndo({
      id: data.id,
      label: data.id + ' = ' + data.value,
      type: 'value',
      prevId: nodes[data.id] ? data.id : null,
      hide: existed ? nodes[data.id].hide : true
    });
    if (!existed || nodes[data.id].hide)
      return true;
    nodes[data.id].changed = true;
    pushUndo(function() {
      nodes[data.id].changed = false;
    });
    executeBeforeNextCommand.pushWithUndo(function() {
      nodes[data.id].changed = false;
      pushUndo(function() {
        nodes[data.id].changed = true;
      });
    });
  },
  enter: function(data) {
    let node = nodes[data.id];
    node.running = true;
    node.start = data.time;
    let oldTimeElapsed = node.timeElapsed;
    node.timeElapsed = null;
    pushUndo(function() {
      node.running = false;
      node.start = null;
      node.timeElapsed = oldTimeElapsed;
    });
  },
  exit: function(data) {
    let node = nodes[data.id];
    node.running = false;
    let oldTimeElapsed = node.timeElapsed;
    node.timeElapsed = time ? (parseFloat(data.time) - parseFloat(node.start)) * 1000 : null;
    pushUndo(function() {
      node.running = true;
      node.timeElapsed = oldTimeElapsed;
    });
  }
};
function processMessage(data, suppressUpdate) {
  if (!callbacks.hasOwnProperty(data.action))
    throw new Error('Unknown action ' + data.action);
  let result = callbacks[data.action].call(callbacks, data);
  if (!suppressUpdate)
    update();
  return result;
}
var executeBeforeNextCommand = [];
function doNext(suppressUpdate) {
  if (!log.length)
    return;
  startUndoScope();
  while (executeBeforeNextCommand.length) {
    executeBeforeNextCommand.shiftWithUndo()();
  }
  while (log.length) {
    let result = (function() {
      let message = log.shift();
      pushUndo(function() {
        log.unshift(message);
      });
      return processMessage(message, suppressUpdate);
    })();
    if (!result)
      break;
  }
  if (!log.length) {
    $('#ended').fadeIn(1500);
    pushUndo(function() {
      $('#ended').hide();
    });
  }
  step++;
  updateTimeline();
  pushUndo(function() {
    step--;
    updateTimeline();
  });
  endUndoScope();
}
function countSteps() {
  if (undoStack.length !== 0) {
    throw new Error(
      'Illegal state; must call countSteps before execution begins');
  }
  let steps = 0;
  while (log.length) {
    doNext();
    steps++;
  }
  while (undoStack.length)
    undoStack.pop()();
  return steps;
}
function updateTimeline() {
  $('#timeline-fill').width((step/totalSteps*100) + '%');
}
function zoom() {
  let scale = d3.event.scale;
  let x = d3.event.translate[0];
  let y = d3.event.translate[1];
  d3.select('#viz').attr('transform', 'scale(' + scale + ') translate(' + x/scale + ' ' + y/scale + ')');
}
// The total number of steps, as far as the user is concerned, in the log.
// This may/will be different than the number of log entries, since each
// step may include more than one log entry.
let totalSteps;
// The current step we're on.
let step;
$(function() {
  d3.select('svg').call(d3.behavior.zoom().scale(4).on('zoom', zoom));
  $(document.body).on('keydown', function(e) {
    if (e.which === 39 || e.which === 32) { // space, right
      // Move one step ahead
      doNext();
    }
    if (e.which === 37) { // left
      // Move one step back
      undo();
    }
    if (e.which === 35) { // end
      // Seek to end
      while (log.length) {
        doNext();
      }
    }
    if (e.which === 36) { // home
      // Seek to beginning
      undoAll();
    }
  });
  // Timeline click and scrub
  $('#timeline').on('click mousemove', function(e) {
    // Make sure left mouse button is down.
    // Firefox is stupid; e.which is always 1 on mousemove events,
    // even when button is not down!! So read e.originalEvent.buttons.
    if (typeof(e.originalEvent.buttons) !== 'undefined') {
      if (e.originalEvent.buttons !== 1)
        return;
    } else if (e.which !== 1) {
      return;
    }
    let timeline = e.currentTarget;
    let pos = e.offsetX || e.originalEvent.layerX;
    let width = timeline.offsetWidth;
    let targetStep = Math.round((pos/width) * totalSteps);
    while (step < targetStep) {
      doNext();
    }
    while (step > targetStep && step != 1) {
      undo();
    }
  });
  totalSteps = countSteps();
  step = 0;
  doNext();
  // don't allow undoing past initial state
  while (undoStack.length)
    undoStack.pop();
  executeBeforeNextCommand.push(function() {
    $('#instructions').fadeOut(1000);
    // It's weird for the instructions to fade back in, so no pushUndo here
  });
});
