{"action":"created","comment":{"url":"https://api.github.com/repos/NVIDIA/DIGITS/pulls/comments/45519466","id":45519466,"diff_hunk":"@@ -0,0 +1,210 @@\n+// Copyright (c) 2015, NVIDIA CORPORATION.  All rights reserved.\n+\n+// class to setup the event handling to add extended selection for a\n+// dataTable. This allows multiple dataTables to function on the same\n+// page. This adds mouse/pointer based click and marquee selection\n+// with shift and ctrl/meta modification for range and toggle\n+// selection, up/down arrow key also with shift and ctrl/meta\n+// modification, and alt-a, alt-d, and alt-i for select-all,\n+// deselect-all, and invert-selection.\n+\n+// Because key events seem to need to go through the document, the\n+// current table is tracked so that the correct table can be\n+// affected. There's a chance that would fail if someone could put a\n+// subdataTable inside of another dataTable, but that's unlikely if\n+// not impossible.\n+\n+// var table = $('#my_table').DataTable();\n+// var ts = new TableSelection('#my_table');\n+\n+function TableSelection(table_id) {\n+    // class state variables\n+    this.last_selected_row = null;\n+    this.mousedown_row = null;\n+    this.mousedown_with_alt = false;\n+    this.mousedown_with_shift = false;\n+    this.mousedown_was_selected = false;\n+    this.mousedown_last_rowIndex = -1;\n+\n+    function on_selection_changed() {\n+        var e = $.Event('selection_changed');\n+        e.count = $(TableSelection.current_table + ' tr.selected').length;\n+        $(window).trigger(e);\n+    }\n+\n+    // Disablew text selection because we want to select rows not text\n+    $(table_id + ' tbody').disableSelection();\n+\n+    // track the current table for key events\n+    $(table_id + ' tbody').mouseenter(function() {\n+        TableSelection.current_table = table_id;\n+    });\n+\n+    // track the current table for key events\n+    $(table_id + ' tbody').mouseleave(function() {\n+        TableSelection.current_table = null;\n+    });\n+\n+    // call this that for use in anonymous functions\n+    var that = this;\n+\n+    // add selected class to item\n+    function select(item) {\n+        if ($(item).hasClass('selected') == false) {\n+            $(item).addClass('selected');\n+            on_selection_changed()\n+        }\n+    }\n+\n+    // remote selected class from item\n+    function deselect(item) {\n+        if ($(item).hasClass('selected')) {\n+            $(item).removeClass('selected');\n+            on_selection_changed();\n+        }\n+    }\n+\n+    // begin a drag selection\n+    $(table_id + ' tbody').on('mousedown', 'tr', function(e) {\n+        that.mousedown_row = this;\n+        that.mousedown_with_alt = (e.ctrlKey || e.metaKey);\n+        that.mousedown_with_shift = (e.shiftKey);\n+        that.mousedown_was_selected = $(this).hasClass('selected');\n+        that.mousedown_last_rowIndex = -1;\n+    });\n+\n+    // drag selection\n+    $(table_id + ' tbody').on('mousemove', 'tr', function(e) {\n+        if (e.which == 1 && that.mousedown_row != null) {\n+\n+            if (that.mousedown_last_rowIndex == this.rowIndex)\n+                return;\n+\n+            var first = Math.min(that.mousedown_row.rowIndex, this.rowIndex) + 1;\n+            var last = Math.max(that.mousedown_row.rowIndex, this.rowIndex) + 1;\n+\n+            // Do we want to add, remove, or replace the selection\n+            var action = (that.mousedown_with_shift ? 'add' :\n+                          that.mousedown_with_alt ? (\n+                              that.mousedown_was_selected ? 'remove' : 'add') :\n+                          'replace');\n+\n+            if (action == 'remove') {\n+                for (var i = first; i <= last; i++)\n+                    deselect($(table_id + ' tr').eq(i));\n+                that.last_selected_row = $(TableSelection.current_table + ' tr.selected').last();\n+            } else {\n+                if (action == 'replace')\n+                    deselect($(table_id + ' tr'))\n+\n+                for (var i = first; i <= last; i++)\n+                    select($(table_id + ' tr').eq(i));\n+\n+                that.last_selected_row = this;\n+            }\n+            that.mousedown_last_rowIndex = this.rowIndex;\n+        }\n+    });\n+\n+    // end drag selection\n+    $(table_id + ' tbody').on('mouseup', 'tr', function() {\n+        that.mousedown_row = null;\n+    });\n+\n+    // Let links continue to work.  Without this, a click would\n+    // only do a row selection.\n+    $(table_id + ' tbody').on('click', 'a', function(e) {\n+        location.href = $(this).attr('href');\n+    });\n+\n+    // click selection\n+    $(table_id + ' tbody').on('click', 'tr', function(e) {\n+        e.preventDefault();\n+        // shift select region\n+        if ((e.shiftKey)) {\n+            if (that.last_selected_row != null) {\n+                var first = Math.min(that.last_selected_row.rowIndex, this.rowIndex) + 1;\n+                var last = Math.max(that.last_selected_row.rowIndex, this.rowIndex) + 1;\n+                for (var i = first; i <= last; i++) {\n+                    select($(table_id + ' tr').eq(i));\n+                }\n+                that.last_selected_row = this;\n+            }\n+        }\n+        // toggle click selection\n+        else if ((e.ctrlKey || e.metaKey)) {\n+            if ($(this).hasClass('selected')) {\n+                deselect($(this));\n+                that.last_selected_row = $(TableSelection.current_table + ' tr.selected').last();\n+            } else {\n+                select($(this));\n+                that.last_selected_row = this;\n+            }\n+        }\n+        // simple unmodified click selection\n+        else {\n+            deselect($(table_id + ' tr'));\n+            select($(this));\n+            that.last_selected_row = this;\n+        }\n+    });\n+\n+    // Because the key event is global, only register once.\n+    if (!TableSelection.initialized) {\n+        TableSelection.initialized = true;\n+        $(document).keydown(function(e) {\n+            if (TableSelection.current_table == null) return;\n+            // Use alt-a for select all rows\n+            if ((e.ctrlKey || e.metaKey) && e.keyCode == 65) {\n+                e.preventDefault();\n+                select($(TableSelection.current_table + ' tr'));\n+                that.last_selected_row = null;\n+            }\n+            // Use alt-d for deselect all rows\n+            if ((e.ctrlKey || e.metaKey) && e.keyCode == 68) {\n+                e.preventDefault();\n+                deselect($(TableSelection.current_table + ' tr'));\n+                that.last_selected_row = null;\n+            }\n+            // Use alt-i for invert selection\n+            if ((e.ctrlKey || e.metaKey) && e.keyCode == 73) {\n+                e.preventDefault();\n+                $(TableSelection.current_table + ' tr.selected').addClass('swap');\n+                $(TableSelection.current_table + ' tr').not('swap').addClass('selected');\n+                $(TableSelection.current_table + ' tr.swap').removeClass('swap selected');","path":"digits/static/js/table_selection.js","position":174,"original_position":174,"commit_id":"79ffc9bed240da700d337ec965b55679f98e3818","original_commit_id":"79ffc9bed240da700d337ec965b55679f98e3818","user":{"login":"gheinrich","id":3889770,"avatar_url":"https://avatars.githubusercontent.com/u/3889770?v=3","gravatar_id":"","url":"https://api.github.com/users/gheinrich","html_url":"https://github.com/gheinrich","followers_url":"https://api.github.com/users/gheinrich/followers","following_url":"https://api.github.com/users/gheinrich/following{/other_user}","gists_url":"https://api.github.com/users/gheinrich/gists{/gist_id}","starred_url":"https://api.github.com/users/gheinrich/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gheinrich/subscriptions","organizations_url":"https://api.github.com/users/gheinrich/orgs","repos_url":"https://api.github.com/users/gheinrich/repos","events_url":"https://api.github.com/users/gheinrich/events{/privacy}","received_events_url":"https://api.github.com/users/gheinrich/received_events","type":"User","site_admin":false},"body":"nice trick!","created_at":"2015-11-20T21:16:41Z","updated_at":"2015-11-20T21:16:41Z","html_url":"https://github.com/NVIDIA/DIGITS/pull/405#discussion_r45519466","pull_request_url":"https://api.github.com/repos/NVIDIA/DIGITS/pulls/405","_links":{"self":{"href":"https://api.github.com/repos/NVIDIA/DIGITS/pulls/comments/45519466"},"html":{"href":"https://github.com/NVIDIA/DIGITS/pull/405#discussion_r45519466"},"pull_request":{"href":"https://api.github.com/repos/NVIDIA/DIGITS/pulls/405"}}},"pull_request":{"url":"https://api.github.com/repos/NVIDIA/DIGITS/pulls/405","id":50010618,"html_url":"https://github.com/NVIDIA/DIGITS/pull/405","diff_url":"https://github.com/NVIDIA/DIGITS/pull/405.diff","patch_url":"https://github.com/NVIDIA/DIGITS/pull/405.patch","issue_url":"https://api.github.com/repos/NVIDIA/DIGITS/issues/405","number":405,"state":"open","locked":false,"title":"Add the ability to delete multiple models at once","user":{"login":"jmancewicz","id":13259615,"avatar_url":"https://avatars.githubusercontent.com/u/13259615?v=3","gravatar_id":"","url":"https://api.github.com/users/jmancewicz","html_url":"https://github.com/jmancewicz","followers_url":"https://api.github.com/users/jmancewicz/followers","following_url":"https://api.github.com/users/jmancewicz/following{/other_user}","gists_url":"https://api.github.com/users/jmancewicz/gists{/gist_id}","starred_url":"https://api.github.com/users/jmancewicz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jmancewicz/subscriptions","organizations_url":"https://api.github.com/users/jmancewicz/orgs","repos_url":"https://api.github.com/users/jmancewicz/repos","events_url":"https://api.github.com/users/jmancewicz/events{/privacy}","received_events_url":"https://api.github.com/users/jmancewicz/received_events","type":"User","site_admin":false},"body":"Added a button to the model details page.  There's a dataTable of existing model jobs which can be filtered so a smaller list. The `Delete Selected Jobs` will then delete the items in the filtered list.","created_at":"2015-11-06T22:05:53Z","updated_at":"2015-11-20T21:16:41Z","closed_at":null,"merged_at":null,"merge_commit_sha":"724b47dda824bf3d7aaf430ac8c250c428f917fd","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/NVIDIA/DIGITS/pulls/405/commits","review_comments_url":"https://api.github.com/repos/NVIDIA/DIGITS/pulls/405/comments","review_comment_url":"https://api.github.com/repos/NVIDIA/DIGITS/pulls/comments{/number}","comments_url":"https://api.github.com/repos/NVIDIA/DIGITS/issues/405/comments","statuses_url":"https://api.github.com/repos/NVIDIA/DIGITS/statuses/79ffc9bed240da700d337ec965b55679f98e3818","head":{"label":"jmancewicz:delete-selected-models","ref":"delete-selected-models","sha":"79ffc9bed240da700d337ec965b55679f98e3818","user":{"login":"jmancewicz","id":13259615,"avatar_url":"https://avatars.githubusercontent.com/u/13259615?v=3","gravatar_id":"","url":"https://api.github.com/users/jmancewicz","html_url":"https://github.com/jmancewicz","followers_url":"https://api.github.com/users/jmancewicz/followers","following_url":"https://api.github.com/users/jmancewicz/following{/other_user}","gists_url":"https://api.github.com/users/jmancewicz/gists{/gist_id}","starred_url":"https://api.github.com/users/jmancewicz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jmancewicz/subscriptions","organizations_url":"https://api.github.com/users/jmancewicz/orgs","repos_url":"https://api.github.com/users/jmancewicz/repos","events_url":"https://api.github.com/users/jmancewicz/events{/privacy}","received_events_url":"https://api.github.com/users/jmancewicz/received_events","type":"User","site_admin":false},"repo":{"id":40381598,"name":"DIGITS","full_name":"jmancewicz/DIGITS","owner":{"login":"jmancewicz","id":13259615,"avatar_url":"https://avatars.githubusercontent.com/u/13259615?v=3","gravatar_id":"","url":"https://api.github.com/users/jmancewicz","html_url":"https://github.com/jmancewicz","followers_url":"https://api.github.com/users/jmancewicz/followers","following_url":"https://api.github.com/users/jmancewicz/following{/other_user}","gists_url":"https://api.github.com/users/jmancewicz/gists{/gist_id}","starred_url":"https://api.github.com/users/jmancewicz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jmancewicz/subscriptions","organizations_url":"https://api.github.com/users/jmancewicz/orgs","repos_url":"https://api.github.com/users/jmancewicz/repos","events_url":"https://api.github.com/users/jmancewicz/events{/privacy}","received_events_url":"https://api.github.com/users/jmancewicz/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/jmancewicz/DIGITS","description":"Deep Learning GPU Training System","fork":true,"url":"https://api.github.com/repos/jmancewicz/DIGITS","forks_url":"https://api.github.com/repos/jmancewicz/DIGITS/forks","keys_url":"https://api.github.com/repos/jmancewicz/DIGITS/keys{/key_id}","collaborators_url":"https://api.github.com/repos/jmancewicz/DIGITS/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/jmancewicz/DIGITS/teams","hooks_url":"https://api.github.com/repos/jmancewicz/DIGITS/hooks","issue_events_url":"https://api.github.com/repos/jmancewicz/DIGITS/issues/events{/number}","events_url":"https://api.github.com/repos/jmancewicz/DIGITS/events","assignees_url":"https://api.github.com/repos/jmancewicz/DIGITS/assignees{/user}","branches_url":"https://api.github.com/repos/jmancewicz/DIGITS/branches{/branch}","tags_url":"https://api.github.com/repos/jmancewicz/DIGITS/tags","blobs_url":"https://api.github.com/repos/jmancewicz/DIGITS/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/jmancewicz/DIGITS/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/jmancewicz/DIGITS/git/refs{/sha}","trees_url":"https://api.github.com/repos/jmancewicz/DIGITS/git/trees{/sha}","statuses_url":"https://api.github.com/repos/jmancewicz/DIGITS/statuses/{sha}","languages_url":"https://api.github.com/repos/jmancewicz/DIGITS/languages","stargazers_url":"https://api.github.com/repos/jmancewicz/DIGITS/stargazers","contributors_url":"https://api.github.com/repos/jmancewicz/DIGITS/contributors","subscribers_url":"https://api.github.com/repos/jmancewicz/DIGITS/subscribers","subscription_url":"https://api.github.com/repos/jmancewicz/DIGITS/subscription","commits_url":"https://api.github.com/repos/jmancewicz/DIGITS/commits{/sha}","git_commits_url":"https://api.github.com/repos/jmancewicz/DIGITS/git/commits{/sha}","comments_url":"https://api.github.com/repos/jmancewicz/DIGITS/comments{/number}","issue_comment_url":"https://api.github.com/repos/jmancewicz/DIGITS/issues/comments{/number}","contents_url":"https://api.github.com/repos/jmancewicz/DIGITS/contents/{+path}","compare_url":"https://api.github.com/repos/jmancewicz/DIGITS/compare/{base}...{head}","merges_url":"https://api.github.com/repos/jmancewicz/DIGITS/merges","archive_url":"https://api.github.com/repos/jmancewicz/DIGITS/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/jmancewicz/DIGITS/downloads","issues_url":"https://api.github.com/repos/jmancewicz/DIGITS/issues{/number}","pulls_url":"https://api.github.com/repos/jmancewicz/DIGITS/pulls{/number}","milestones_url":"https://api.github.com/repos/jmancewicz/DIGITS/milestones{/number}","notifications_url":"https://api.github.com/repos/jmancewicz/DIGITS/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/jmancewicz/DIGITS/labels{/name}","releases_url":"https://api.github.com/repos/jmancewicz/DIGITS/releases{/id}","created_at":"2015-08-07T21:27:57Z","updated_at":"2015-08-07T21:27:58Z","pushed_at":"2015-11-20T20:15:51Z","git_url":"git://github.com/jmancewicz/DIGITS.git","ssh_url":"git@github.com:jmancewicz/DIGITS.git","clone_url":"https://github.com/jmancewicz/DIGITS.git","svn_url":"https://github.com/jmancewicz/DIGITS","homepage":"https://developer.nvidia.com/digits","size":4767,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"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":"NVIDIA:master","ref":"master","sha":"9edc2adb79a539e5c89de19f527a2648521052c3","user":{"login":"NVIDIA","id":1728152,"avatar_url":"https://avatars.githubusercontent.com/u/1728152?v=3","gravatar_id":"","url":"https://api.github.com/users/NVIDIA","html_url":"https://github.com/NVIDIA","followers_url":"https://api.github.com/users/NVIDIA/followers","following_url":"https://api.github.com/users/NVIDIA/following{/other_user}","gists_url":"https://api.github.com/users/NVIDIA/gists{/gist_id}","starred_url":"https://api.github.com/users/NVIDIA/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/NVIDIA/subscriptions","organizations_url":"https://api.github.com/users/NVIDIA/orgs","repos_url":"https://api.github.com/users/NVIDIA/repos","events_url":"https://api.github.com/users/NVIDIA/events{/privacy}","received_events_url":"https://api.github.com/users/NVIDIA/received_events","type":"Organization","site_admin":false},"repo":{"id":32403967,"name":"DIGITS","full_name":"NVIDIA/DIGITS","owner":{"login":"NVIDIA","id":1728152,"avatar_url":"https://avatars.githubusercontent.com/u/1728152?v=3","gravatar_id":"","url":"https://api.github.com/users/NVIDIA","html_url":"https://github.com/NVIDIA","followers_url":"https://api.github.com/users/NVIDIA/followers","following_url":"https://api.github.com/users/NVIDIA/following{/other_user}","gists_url":"https://api.github.com/users/NVIDIA/gists{/gist_id}","starred_url":"https://api.github.com/users/NVIDIA/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/NVIDIA/subscriptions","organizations_url":"https://api.github.com/users/NVIDIA/orgs","repos_url":"https://api.github.com/users/NVIDIA/repos","events_url":"https://api.github.com/users/NVIDIA/events{/privacy}","received_events_url":"https://api.github.com/users/NVIDIA/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/NVIDIA/DIGITS","description":"Deep Learning GPU Training System","fork":false,"url":"https://api.github.com/repos/NVIDIA/DIGITS","forks_url":"https://api.github.com/repos/NVIDIA/DIGITS/forks","keys_url":"https://api.github.com/repos/NVIDIA/DIGITS/keys{/key_id}","collaborators_url":"https://api.github.com/repos/NVIDIA/DIGITS/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/NVIDIA/DIGITS/teams","hooks_url":"https://api.github.com/repos/NVIDIA/DIGITS/hooks","issue_events_url":"https://api.github.com/repos/NVIDIA/DIGITS/issues/events{/number}","events_url":"https://api.github.com/repos/NVIDIA/DIGITS/events","assignees_url":"https://api.github.com/repos/NVIDIA/DIGITS/assignees{/user}","branches_url":"https://api.github.com/repos/NVIDIA/DIGITS/branches{/branch}","tags_url":"https://api.github.com/repos/NVIDIA/DIGITS/tags","blobs_url":"https://api.github.com/repos/NVIDIA/DIGITS/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/NVIDIA/DIGITS/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/NVIDIA/DIGITS/git/refs{/sha}","trees_url":"https://api.github.com/repos/NVIDIA/DIGITS/git/trees{/sha}","statuses_url":"https://api.github.com/repos/NVIDIA/DIGITS/statuses/{sha}","languages_url":"https://api.github.com/repos/NVIDIA/DIGITS/languages","stargazers_url":"https://api.github.com/repos/NVIDIA/DIGITS/stargazers","contributors_url":"https://api.github.com/repos/NVIDIA/DIGITS/contributors","subscribers_url":"https://api.github.com/repos/NVIDIA/DIGITS/subscribers","subscription_url":"https://api.github.com/repos/NVIDIA/DIGITS/subscription","commits_url":"https://api.github.com/repos/NVIDIA/DIGITS/commits{/sha}","git_commits_url":"https://api.github.com/repos/NVIDIA/DIGITS/git/commits{/sha}","comments_url":"https://api.github.com/repos/NVIDIA/DIGITS/comments{/number}","issue_comment_url":"https://api.github.com/repos/NVIDIA/DIGITS/issues/comments{/number}","contents_url":"https://api.github.com/repos/NVIDIA/DIGITS/contents/{+path}","compare_url":"https://api.github.com/repos/NVIDIA/DIGITS/compare/{base}...{head}","merges_url":"https://api.github.com/repos/NVIDIA/DIGITS/merges","archive_url":"https://api.github.com/repos/NVIDIA/DIGITS/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/NVIDIA/DIGITS/downloads","issues_url":"https://api.github.com/repos/NVIDIA/DIGITS/issues{/number}","pulls_url":"https://api.github.com/repos/NVIDIA/DIGITS/pulls{/number}","milestones_url":"https://api.github.com/repos/NVIDIA/DIGITS/milestones{/number}","notifications_url":"https://api.github.com/repos/NVIDIA/DIGITS/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/NVIDIA/DIGITS/labels{/name}","releases_url":"https://api.github.com/repos/NVIDIA/DIGITS/releases{/id}","created_at":"2015-03-17T16:00:20Z","updated_at":"2015-11-20T20:42:47Z","pushed_at":"2015-11-20T20:36:12Z","git_url":"git://github.com/NVIDIA/DIGITS.git","ssh_url":"git@github.com:NVIDIA/DIGITS.git","clone_url":"https://github.com/NVIDIA/DIGITS.git","svn_url":"https://github.com/NVIDIA/DIGITS","homepage":"https://developer.nvidia.com/digits","size":10228,"stargazers_count":783,"watchers_count":783,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":221,"mirror_url":null,"open_issues_count":56,"forks":221,"open_issues":56,"watchers":783,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/NVIDIA/DIGITS/pulls/405"},"html":{"href":"https://github.com/NVIDIA/DIGITS/pull/405"},"issue":{"href":"https://api.github.com/repos/NVIDIA/DIGITS/issues/405"},"comments":{"href":"https://api.github.com/repos/NVIDIA/DIGITS/issues/405/comments"},"review_comments":{"href":"https://api.github.com/repos/NVIDIA/DIGITS/pulls/405/comments"},"review_comment":{"href":"https://api.github.com/repos/NVIDIA/DIGITS/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/NVIDIA/DIGITS/pulls/405/commits"},"statuses":{"href":"https://api.github.com/repos/NVIDIA/DIGITS/statuses/79ffc9bed240da700d337ec965b55679f98e3818"}}}}