{"action":"created","comment":{"url":"https://api.github.com/repos/Scribery/cockpit/pulls/comments/142151837","pull_request_review_id":66471105,"id":142151837,"diff_hunk":"@@ -0,0 +1,823 @@\n+/*\n+ * This file is part of Cockpit.\n+ *\n+ * Copyright (C) 2017 Red Hat, Inc.\n+ *\n+ * Cockpit is free software; you can redistribute it and/or modify it\n+ * under the terms of the GNU Lesser General Public License as published by\n+ * the Free Software Foundation; either version 2.1 of the License, or\n+ * (at your option) any later version.\n+ *\n+ * Cockpit is distributed in the hope that it will be useful, but\n+ * WITHOUT ANY WARRANTY; without even the implied warranty of\n+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n+ * Lesser General Public License for more details.\n+ *\n+ * You should have received a copy of the GNU Lesser General Public License\n+ * along with Cockpit; If not, see <http://www.gnu.org/licenses/>.\n+ */\n+\n+(function() {\n+    \"use strict\";\n+\n+    let cockpit = require(\"cockpit\");\n+    let _ = cockpit.gettext;\n+    let React = require(\"react\");\n+    let Term = require(\"term\");\n+    let Journal = require(\"journal\");\n+    let $ = require(\"jquery\");\n+\n+    require(\"console.css\");\n+\n+    /*\n+     * Get an object field, verifying its presence and type.\n+     */\n+    let getValidField = function (object, field, type) {\n+        let value;\n+        if (!(field in object)) {\n+            throw \"\\\"\" + field + \"\\\" field is missing\";\n+        }\n+        value = object[field];\n+        if (typeof(value) != type) {\n+            throw \"invalid \\\"\" + field + \"\\\" field type: \" + typeof(value);\n+        }\n+        return value;\n+    }\n+\n+    /*\n+     * An auto-loading buffer of recording's packets.\n+     */\n+    let PacketBuffer = class {\n+        /*\n+         * Initialize a buffer.\n+         */\n+        constructor(matchList) {\n+            this.handleError = this.handleError.bind(this);\n+            this.handleStream = this.handleStream.bind(this);\n+            this.handleDone = this.handleDone.bind(this);\n+            /* RegExp used to parse message's timing field */\n+            this.timingRE = new RegExp(\n+                                    /* Delay (1) */\n+                                    \"\\\\+(\\\\d+)|\" +\n+                                    /* Text input (2) */\n+                                    \"<(\\\\d+)|\" +\n+                                    /* Binary input (3, 4) */\n+                                    \"\\\\[(\\\\d+)/(\\\\d+)|\" +\n+                                    /* Text output (5) */\n+                                    \">(\\\\d+)|\" +\n+                                    /* Binary output (6, 7) */\n+                                    \"\\\\](\\\\d+)/(\\\\d+)|\" +\n+                                    /* Window (8, 9) */\n+                                    \"=(\\\\d+)x(\\\\d+)|\" +\n+                                    /* End of string */\n+                                    \"$\",\n+                                    /* Continue after the last match only */\n+                                    /* FIXME Support likely sparse */\n+                                    \"y\"\n+            );\n+            /* List of matches to apply when loading the buffer from Journal */\n+            this.matchList = matchList;\n+            /*\n+             * An array of two-element arrays (tuples) each containing a\n+             * packet index and a deferred object. The list is kept sorted to\n+             * have tuples with lower packet indices first. Once the buffer\n+             * receives a packet at the specified index, the matching tuple is\n+             * removed from the list, and its deferred object is resolved.\n+             * This is used to keep users informed about packets arriving.\n+             */\n+            this.idxDfdList = [];\n+            /* Last seen message ID */\n+            this.id = 0;\n+            /* Last seen time position */\n+            this.pos = 0;\n+            /* Last seen window width */\n+            this.width = null;\n+            /* Last seen window height */\n+            this.height = null;\n+            /* List of packets read */\n+            this.pktList = [];\n+            /* Error which stopped the loading */\n+            this.error = null;\n+            /* The journalctl reading the recording */\n+            this.journalctl = Journal.journalctl(\n+                                this.matchList,\n+                                {count: \"all\", follow: false});\n+            this.journalctl.fail(this.handleError);\n+            this.journalctl.stream(this.handleStream);\n+            this.journalctl.done(this.handleDone);\n+            /*\n+             * Last seen cursor of the first, non-follow, journalctl run.\n+             * Null if no entry was received yet, or the second run has\n+             * skipped the entry received last by the first run.\n+             */\n+            this.cursor = null;\n+            /* True if the first, non-follow, journalctl run has completed */\n+            this.done = false;\n+        }\n+\n+        /*\n+         * Return a promise which is resolved when a packet at a particular\n+         * index is received by the buffer. The promise is rejected with a\n+         * non-null argument if an error occurs or has occurred previously.\n+         * The promise is rejected with null, when the buffer is stopped. If\n+         * the packet index is not specified, assume it's the next packet.\n+         */\n+        awaitPacket(idx) {\n+            let i;\n+            let idxDfd;\n+\n+            /* If an error has occurred previously */\n+            if (this.error !== null) {\n+                /* Reject immediately */\n+                return $.Deferred().reject(this.error).promise();\n+            }\n+\n+            /* If the buffer was stopped */\n+            if (this.journalctl === null) {\n+                return $.Deferred().reject(null).promise();\n+            }\n+\n+            /* If packet index is not specified */\n+            if (idx === undefined) {\n+                /* Assume it's the next one */\n+                idx = this.pktList.length;\n+            } else {\n+                /* If it has already been received */\n+                if (idx < this.pktList.length) {\n+                    /* Return resolved promise */\n+                    return $.Deferred().resolve().promise();\n+                }\n+            }\n+\n+            /* Try to find an existing, matching tuple */\n+            for (i = 0; i < this.idxDfdList.length; i++) {","path":"pkg/session_recording/player.jsx","position":153,"original_position":153,"commit_id":"f6085c9cc31aa46eee161b11cbb353fff5ad3742","original_commit_id":"f6085c9cc31aa46eee161b11cbb353fff5ad3742","user":{"login":"sabbaka","id":5558501,"avatar_url":"https://avatars0.githubusercontent.com/u/5558501?v=4","gravatar_id":"","url":"https://api.github.com/users/sabbaka","html_url":"https://github.com/sabbaka","followers_url":"https://api.github.com/users/sabbaka/followers","following_url":"https://api.github.com/users/sabbaka/following{/other_user}","gists_url":"https://api.github.com/users/sabbaka/gists{/gist_id}","starred_url":"https://api.github.com/users/sabbaka/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sabbaka/subscriptions","organizations_url":"https://api.github.com/users/sabbaka/orgs","repos_url":"https://api.github.com/users/sabbaka/repos","events_url":"https://api.github.com/users/sabbaka/events{/privacy}","received_events_url":"https://api.github.com/users/sabbaka/received_events","type":"User","site_admin":false},"body":"Tuple here consists from different packets, right? ","created_at":"2017-10-02T14:20:17Z","updated_at":"2017-10-02T14:20:18Z","html_url":"https://github.com/Scribery/cockpit/pull/38#discussion_r142151837","pull_request_url":"https://api.github.com/repos/Scribery/cockpit/pulls/38","author_association":"NONE","_links":{"self":{"href":"https://api.github.com/repos/Scribery/cockpit/pulls/comments/142151837"},"html":{"href":"https://github.com/Scribery/cockpit/pull/38#discussion_r142151837"},"pull_request":{"href":"https://api.github.com/repos/Scribery/cockpit/pulls/38"}}},"pull_request":{"url":"https://api.github.com/repos/Scribery/cockpit/pulls/38","id":141902225,"html_url":"https://github.com/Scribery/cockpit/pull/38","diff_url":"https://github.com/Scribery/cockpit/pull/38.diff","patch_url":"https://github.com/Scribery/cockpit/pull/38.patch","issue_url":"https://api.github.com/repos/Scribery/cockpit/issues/38","number":38,"state":"open","locked":false,"title":"Add browser-based player","user":{"login":"spbnick","id":549246,"avatar_url":"https://avatars3.githubusercontent.com/u/549246?v=4","gravatar_id":"","url":"https://api.github.com/users/spbnick","html_url":"https://github.com/spbnick","followers_url":"https://api.github.com/users/spbnick/followers","following_url":"https://api.github.com/users/spbnick/following{/other_user}","gists_url":"https://api.github.com/users/spbnick/gists{/gist_id}","starred_url":"https://api.github.com/users/spbnick/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/spbnick/subscriptions","organizations_url":"https://api.github.com/users/spbnick/orgs","repos_url":"https://api.github.com/users/spbnick/repos","events_url":"https://api.github.com/users/spbnick/events{/privacy}","received_events_url":"https://api.github.com/users/spbnick/received_events","type":"User","site_admin":false},"body":"This is a hacked-together attempt at implementing a browser-based player, just a starter for discussion for now.","created_at":"2017-09-19T16:52:33Z","updated_at":"2017-10-02T14:20:18Z","closed_at":null,"merged_at":null,"merge_commit_sha":"66c87fd9bea11e9e34bbf66156f45baa94493ec6","assignee":null,"assignees":[],"requested_reviewers":[],"milestone":null,"commits_url":"https://api.github.com/repos/Scribery/cockpit/pulls/38/commits","review_comments_url":"https://api.github.com/repos/Scribery/cockpit/pulls/38/comments","review_comment_url":"https://api.github.com/repos/Scribery/cockpit/pulls/comments{/number}","comments_url":"https://api.github.com/repos/Scribery/cockpit/issues/38/comments","statuses_url":"https://api.github.com/repos/Scribery/cockpit/statuses/f6085c9cc31aa46eee161b11cbb353fff5ad3742","head":{"label":"spbnick:tlog-play.js","ref":"tlog-play.js","sha":"f6085c9cc31aa46eee161b11cbb353fff5ad3742","user":{"login":"spbnick","id":549246,"avatar_url":"https://avatars3.githubusercontent.com/u/549246?v=4","gravatar_id":"","url":"https://api.github.com/users/spbnick","html_url":"https://github.com/spbnick","followers_url":"https://api.github.com/users/spbnick/followers","following_url":"https://api.github.com/users/spbnick/following{/other_user}","gists_url":"https://api.github.com/users/spbnick/gists{/gist_id}","starred_url":"https://api.github.com/users/spbnick/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/spbnick/subscriptions","organizations_url":"https://api.github.com/users/spbnick/orgs","repos_url":"https://api.github.com/users/spbnick/repos","events_url":"https://api.github.com/users/spbnick/events{/privacy}","received_events_url":"https://api.github.com/users/spbnick/received_events","type":"User","site_admin":false},"repo":{"id":103968865,"name":"cockpit","full_name":"spbnick/cockpit","owner":{"login":"spbnick","id":549246,"avatar_url":"https://avatars3.githubusercontent.com/u/549246?v=4","gravatar_id":"","url":"https://api.github.com/users/spbnick","html_url":"https://github.com/spbnick","followers_url":"https://api.github.com/users/spbnick/followers","following_url":"https://api.github.com/users/spbnick/following{/other_user}","gists_url":"https://api.github.com/users/spbnick/gists{/gist_id}","starred_url":"https://api.github.com/users/spbnick/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/spbnick/subscriptions","organizations_url":"https://api.github.com/users/spbnick/orgs","repos_url":"https://api.github.com/users/spbnick/repos","events_url":"https://api.github.com/users/spbnick/events{/privacy}","received_events_url":"https://api.github.com/users/spbnick/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/spbnick/cockpit","description":"Cockpit fork for work on Session Recording UI","fork":true,"url":"https://api.github.com/repos/spbnick/cockpit","forks_url":"https://api.github.com/repos/spbnick/cockpit/forks","keys_url":"https://api.github.com/repos/spbnick/cockpit/keys{/key_id}","collaborators_url":"https://api.github.com/repos/spbnick/cockpit/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/spbnick/cockpit/teams","hooks_url":"https://api.github.com/repos/spbnick/cockpit/hooks","issue_events_url":"https://api.github.com/repos/spbnick/cockpit/issues/events{/number}","events_url":"https://api.github.com/repos/spbnick/cockpit/events","assignees_url":"https://api.github.com/repos/spbnick/cockpit/assignees{/user}","branches_url":"https://api.github.com/repos/spbnick/cockpit/branches{/branch}","tags_url":"https://api.github.com/repos/spbnick/cockpit/tags","blobs_url":"https://api.github.com/repos/spbnick/cockpit/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/spbnick/cockpit/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/spbnick/cockpit/git/refs{/sha}","trees_url":"https://api.github.com/repos/spbnick/cockpit/git/trees{/sha}","statuses_url":"https://api.github.com/repos/spbnick/cockpit/statuses/{sha}","languages_url":"https://api.github.com/repos/spbnick/cockpit/languages","stargazers_url":"https://api.github.com/repos/spbnick/cockpit/stargazers","contributors_url":"https://api.github.com/repos/spbnick/cockpit/contributors","subscribers_url":"https://api.github.com/repos/spbnick/cockpit/subscribers","subscription_url":"https://api.github.com/repos/spbnick/cockpit/subscription","commits_url":"https://api.github.com/repos/spbnick/cockpit/commits{/sha}","git_commits_url":"https://api.github.com/repos/spbnick/cockpit/git/commits{/sha}","comments_url":"https://api.github.com/repos/spbnick/cockpit/comments{/number}","issue_comment_url":"https://api.github.com/repos/spbnick/cockpit/issues/comments{/number}","contents_url":"https://api.github.com/repos/spbnick/cockpit/contents/{+path}","compare_url":"https://api.github.com/repos/spbnick/cockpit/compare/{base}...{head}","merges_url":"https://api.github.com/repos/spbnick/cockpit/merges","archive_url":"https://api.github.com/repos/spbnick/cockpit/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/spbnick/cockpit/downloads","issues_url":"https://api.github.com/repos/spbnick/cockpit/issues{/number}","pulls_url":"https://api.github.com/repos/spbnick/cockpit/pulls{/number}","milestones_url":"https://api.github.com/repos/spbnick/cockpit/milestones{/number}","notifications_url":"https://api.github.com/repos/spbnick/cockpit/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/spbnick/cockpit/labels{/name}","releases_url":"https://api.github.com/repos/spbnick/cockpit/releases{/id}","deployments_url":"https://api.github.com/repos/spbnick/cockpit/deployments","created_at":"2017-09-18T17:15:50Z","updated_at":"2017-09-18T17:15:56Z","pushed_at":"2017-09-27T07:46:26Z","git_url":"git://github.com/spbnick/cockpit.git","ssh_url":"git@github.com:spbnick/cockpit.git","clone_url":"https://github.com/spbnick/cockpit.git","svn_url":"https://github.com/spbnick/cockpit","homepage":"http://www.cockpit-project.org/","size":34467,"stargazers_count":0,"watchers_count":0,"language":"JavaScript","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"Scribery:scribery_poc","ref":"scribery_poc","sha":"6fba50c72889912ff609474352ed720b8e7014c5","user":{"login":"Scribery","id":17722920,"avatar_url":"https://avatars2.githubusercontent.com/u/17722920?v=4","gravatar_id":"","url":"https://api.github.com/users/Scribery","html_url":"https://github.com/Scribery","followers_url":"https://api.github.com/users/Scribery/followers","following_url":"https://api.github.com/users/Scribery/following{/other_user}","gists_url":"https://api.github.com/users/Scribery/gists{/gist_id}","starred_url":"https://api.github.com/users/Scribery/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Scribery/subscriptions","organizations_url":"https://api.github.com/users/Scribery/orgs","repos_url":"https://api.github.com/users/Scribery/repos","events_url":"https://api.github.com/users/Scribery/events{/privacy}","received_events_url":"https://api.github.com/users/Scribery/received_events","type":"Organization","site_admin":false},"repo":{"id":96337975,"name":"cockpit","full_name":"Scribery/cockpit","owner":{"login":"Scribery","id":17722920,"avatar_url":"https://avatars2.githubusercontent.com/u/17722920?v=4","gravatar_id":"","url":"https://api.github.com/users/Scribery","html_url":"https://github.com/Scribery","followers_url":"https://api.github.com/users/Scribery/followers","following_url":"https://api.github.com/users/Scribery/following{/other_user}","gists_url":"https://api.github.com/users/Scribery/gists{/gist_id}","starred_url":"https://api.github.com/users/Scribery/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Scribery/subscriptions","organizations_url":"https://api.github.com/users/Scribery/orgs","repos_url":"https://api.github.com/users/Scribery/repos","events_url":"https://api.github.com/users/Scribery/events{/privacy}","received_events_url":"https://api.github.com/users/Scribery/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/Scribery/cockpit","description":"Cockpit fork for work on Session Recording UI","fork":true,"url":"https://api.github.com/repos/Scribery/cockpit","forks_url":"https://api.github.com/repos/Scribery/cockpit/forks","keys_url":"https://api.github.com/repos/Scribery/cockpit/keys{/key_id}","collaborators_url":"https://api.github.com/repos/Scribery/cockpit/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/Scribery/cockpit/teams","hooks_url":"https://api.github.com/repos/Scribery/cockpit/hooks","issue_events_url":"https://api.github.com/repos/Scribery/cockpit/issues/events{/number}","events_url":"https://api.github.com/repos/Scribery/cockpit/events","assignees_url":"https://api.github.com/repos/Scribery/cockpit/assignees{/user}","branches_url":"https://api.github.com/repos/Scribery/cockpit/branches{/branch}","tags_url":"https://api.github.com/repos/Scribery/cockpit/tags","blobs_url":"https://api.github.com/repos/Scribery/cockpit/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/Scribery/cockpit/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/Scribery/cockpit/git/refs{/sha}","trees_url":"https://api.github.com/repos/Scribery/cockpit/git/trees{/sha}","statuses_url":"https://api.github.com/repos/Scribery/cockpit/statuses/{sha}","languages_url":"https://api.github.com/repos/Scribery/cockpit/languages","stargazers_url":"https://api.github.com/repos/Scribery/cockpit/stargazers","contributors_url":"https://api.github.com/repos/Scribery/cockpit/contributors","subscribers_url":"https://api.github.com/repos/Scribery/cockpit/subscribers","subscription_url":"https://api.github.com/repos/Scribery/cockpit/subscription","commits_url":"https://api.github.com/repos/Scribery/cockpit/commits{/sha}","git_commits_url":"https://api.github.com/repos/Scribery/cockpit/git/commits{/sha}","comments_url":"https://api.github.com/repos/Scribery/cockpit/comments{/number}","issue_comment_url":"https://api.github.com/repos/Scribery/cockpit/issues/comments{/number}","contents_url":"https://api.github.com/repos/Scribery/cockpit/contents/{+path}","compare_url":"https://api.github.com/repos/Scribery/cockpit/compare/{base}...{head}","merges_url":"https://api.github.com/repos/Scribery/cockpit/merges","archive_url":"https://api.github.com/repos/Scribery/cockpit/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/Scribery/cockpit/downloads","issues_url":"https://api.github.com/repos/Scribery/cockpit/issues{/number}","pulls_url":"https://api.github.com/repos/Scribery/cockpit/pulls{/number}","milestones_url":"https://api.github.com/repos/Scribery/cockpit/milestones{/number}","notifications_url":"https://api.github.com/repos/Scribery/cockpit/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/Scribery/cockpit/labels{/name}","releases_url":"https://api.github.com/repos/Scribery/cockpit/releases{/id}","deployments_url":"https://api.github.com/repos/Scribery/cockpit/deployments","created_at":"2017-07-05T16:08:47Z","updated_at":"2017-07-13T21:05:31Z","pushed_at":"2017-09-27T12:02:48Z","git_url":"git://github.com/Scribery/cockpit.git","ssh_url":"git@github.com:Scribery/cockpit.git","clone_url":"https://github.com/Scribery/cockpit.git","svn_url":"https://github.com/Scribery/cockpit","homepage":"http://www.cockpit-project.org/","size":34456,"stargazers_count":1,"watchers_count":1,"language":"JavaScript","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":2,"mirror_url":null,"open_issues_count":35,"forks":2,"open_issues":35,"watchers":1,"default_branch":"scribery_poc"}},"_links":{"self":{"href":"https://api.github.com/repos/Scribery/cockpit/pulls/38"},"html":{"href":"https://github.com/Scribery/cockpit/pull/38"},"issue":{"href":"https://api.github.com/repos/Scribery/cockpit/issues/38"},"comments":{"href":"https://api.github.com/repos/Scribery/cockpit/issues/38/comments"},"review_comments":{"href":"https://api.github.com/repos/Scribery/cockpit/pulls/38/comments"},"review_comment":{"href":"https://api.github.com/repos/Scribery/cockpit/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/Scribery/cockpit/pulls/38/commits"},"statuses":{"href":"https://api.github.com/repos/Scribery/cockpit/statuses/f6085c9cc31aa46eee161b11cbb353fff5ad3742"}},"author_association":"OWNER"}}
{"actor":{"display_login":"sabbaka"}}