[ Index ] |
PHP Cross Reference of Phabricator |
[Summary view] [Print] [Text view]
1 /** 2 * @provides javelin-behavior-differential-keyboard-navigation 3 * @requires javelin-behavior 4 * javelin-dom 5 * javelin-stratcom 6 * phabricator-keyboard-shortcut 7 */ 8 9 JX.behavior('differential-keyboard-navigation', function(config) { 10 11 var cursor = -1; 12 var changesets; 13 14 var selection_begin = null; 15 var selection_end = null; 16 17 var refreshFocus = function() {}; 18 19 function init() { 20 if (changesets) { 21 return; 22 } 23 changesets = JX.DOM.scry(document.body, 'div', 'differential-changeset'); 24 } 25 26 function getBlocks(cursor) { 27 // TODO: This might not be terribly fast; we can't currently memoize it 28 // because it can change as ajax requests come in (e.g., content loads). 29 30 var rows = JX.DOM.scry(changesets[cursor], 'tr'); 31 var blocks = [[changesets[cursor], changesets[cursor]]]; 32 var start = null; 33 var type; 34 var ii; 35 36 // Don't show code blocks inside a collapsed file. 37 var diff = JX.DOM.scry(changesets[cursor], 'table', 'differential-diff'); 38 if (diff.length == 1 && JX.Stratcom.getData(diff[0]).hidden) { 39 return blocks; 40 } 41 42 function push() { 43 if (start) { 44 blocks.push([start, rows[ii - 1]]); 45 } 46 start = null; 47 } 48 49 for (ii = 0; ii < rows.length; ii++) { 50 type = getRowType(rows[ii]); 51 if (type == 'comment') { 52 // If we see these types of rows, make a block for each one. 53 push(); 54 } 55 if (!type) { 56 push(); 57 } else if (type && !start) { 58 start = rows[ii]; 59 } 60 } 61 push(); 62 63 return blocks; 64 } 65 66 function getRowType(row) { 67 // NOTE: Being somewhat over-general here to allow other types of objects 68 // to be easily focused in the future (inline comments, 'show more..'). 69 70 if (row.className.indexOf('inline') !== -1) { 71 return 'comment'; 72 } 73 74 if (row.className.indexOf('differential-changeset') !== -1) { 75 return 'file'; 76 } 77 78 var cells = JX.DOM.scry(row, 'td'); 79 80 for (var ii = 0; ii < cells.length; ii++) { 81 // NOTE: The semantic use of classnames here is for performance; don't 82 // emulate this elsewhere since it's super terrible. 83 if (cells[ii].className.indexOf('old') !== -1 || 84 cells[ii].className.indexOf('new') !== -1) { 85 return 'change'; 86 } 87 } 88 89 return null; 90 } 91 92 function jump(manager, delta, jump_to_type) { 93 init(); 94 95 if (cursor < 0) { 96 if (delta < 0) { 97 // If the user goes "back" without a selection, just reject the action. 98 return; 99 } else { 100 cursor = 0; 101 } 102 } 103 104 while (true) { 105 var blocks = getBlocks(cursor); 106 var focus; 107 if (delta < 0) { 108 focus = blocks.length; 109 } else { 110 focus = -1; 111 } 112 113 for (var ii = 0; ii < blocks.length; ii++) { 114 if (blocks[ii][0] == selection_begin) { 115 focus = ii; 116 break; 117 } 118 } 119 120 while (true) { 121 focus += delta; 122 123 if (blocks[focus]) { 124 var row_type = getRowType(blocks[focus][0]); 125 if (jump_to_type && row_type != jump_to_type) { 126 continue; 127 } 128 129 selection_begin = blocks[focus][0]; 130 selection_end = blocks[focus][1]; 131 132 manager.scrollTo(selection_begin); 133 134 refreshFocus = function() { 135 manager.focusOn(selection_begin, selection_end); 136 }; 137 138 refreshFocus(); 139 140 return; 141 } else { 142 var adjusted = (cursor + delta); 143 if (adjusted < 0 || adjusted >= changesets.length) { 144 // Stop cursor movement when the user reaches either end. 145 return; 146 } 147 cursor = adjusted; 148 149 // Break the inner loop and go to the next file. 150 break; 151 } 152 } 153 } 154 155 } 156 157 // When inline comments are updated, wipe out our cache of blocks since 158 // comments may have been added or deleted. 159 JX.Stratcom.listen( 160 null, 161 'differential-inline-comment-update', 162 function() { 163 changesets = null; 164 }); 165 // Same thing when a file is hidden or shown; don't want to highlight 166 // invisible code. 167 JX.Stratcom.listen( 168 'differential-toggle-file-toggled', 169 null, 170 function() { 171 changesets = null; 172 init(); 173 refreshFocus(); 174 }); 175 176 var haunt_mode = 0; 177 function haunt() { 178 haunt_mode = (haunt_mode + 1) % 3; 179 180 var el = JX.$(config.haunt); 181 for (var ii = 1; ii <= 2; ii++) { 182 JX.DOM.alterClass(el, 'differential-haunt-mode-'+ii, (haunt_mode == ii)); 183 } 184 } 185 186 new JX.KeyboardShortcut('j', 'Jump to next change.') 187 .setHandler(function(manager) { 188 jump(manager, 1); 189 }) 190 .register(); 191 192 new JX.KeyboardShortcut('k', 'Jump to previous change.') 193 .setHandler(function(manager) { 194 jump(manager, -1); 195 }) 196 .register(); 197 198 new JX.KeyboardShortcut('J', 'Jump to next file.') 199 .setHandler(function(manager) { 200 jump(manager, 1, 'file'); 201 }) 202 .register(); 203 204 new JX.KeyboardShortcut('K', 'Jump to previous file.') 205 .setHandler(function(manager) { 206 jump(manager, -1, 'file'); 207 }) 208 .register(); 209 210 new JX.KeyboardShortcut('n', 'Jump to next inline comment.') 211 .setHandler(function(manager) { 212 jump(manager, 1, 'comment'); 213 }) 214 .register(); 215 216 new JX.KeyboardShortcut('p', 'Jump to previous inline comment.') 217 .setHandler(function(manager) { 218 jump(manager, -1, 'comment'); 219 }) 220 .register(); 221 222 223 new JX.KeyboardShortcut('t', 'Jump to the table of contents.') 224 .setHandler(function(manager) { 225 var toc = JX.$('toc'); 226 manager.scrollTo(toc); 227 }) 228 .register(); 229 230 new JX.KeyboardShortcut( 231 'h', 232 'Collapse or expand the file display (after jump).') 233 .setHandler(function() { 234 if (!changesets || !changesets[cursor]) { 235 return; 236 } 237 JX.Stratcom.invoke('differential-toggle-file', null, { 238 diff: JX.DOM.scry(changesets[cursor], 'table', 'differential-diff') 239 }); 240 }) 241 .register(); 242 243 244 function inline_op(node, op) { 245 // nothing selected 246 if (!node) { 247 return; 248 } 249 if (!JX.DOM.scry(node, 'a', 'differential-inline-' + op)) { 250 // No link for this operation, e.g. editing a comment you can't edit. 251 return; 252 } 253 254 var data = { 255 node: JX.DOM.find(node, 'div', 'differential-inline-comment'), 256 op: op 257 }; 258 259 JX.Stratcom.invoke('differential-inline-action', null, data); 260 } 261 262 new JX.KeyboardShortcut('r', 'Reply to selected inline comment.') 263 .setHandler(function() { 264 inline_op(selection_begin, 'reply'); 265 }) 266 .register(); 267 268 new JX.KeyboardShortcut('e', 'Edit selected inline comment.') 269 .setHandler(function() { 270 inline_op(selection_begin, 'edit'); 271 }) 272 .register(); 273 274 if (config.haunt) { 275 new JX.KeyboardShortcut('z', 'Cycle comment panel haunting modes.') 276 .setHandler(haunt) 277 .register(); 278 } 279 280 });
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Sun Nov 30 09:20:46 2014 | Cross-referenced by PHPXref 0.7.1 |