├── LICENSE ├── README.mkd └── googlepluscommander.user.js /LICENSE: -------------------------------------------------------------------------------- 1 | NYSL Version 0.9982 2 | ---------------------------------------- 3 | 4 | A. 本ソフトウェアは Everyone'sWare です。このソフトを手にした一人一人が、 5 | ご自分の作ったものを扱うのと同じように、自由に利用することが出来ます。 6 | 7 | A-1. フリーウェアです。作者からは使用料等を要求しません。 8 | A-2. 有料無料や媒体の如何を問わず、自由に転載・再配布できます。 9 | A-3. いかなる種類の 改変・他プログラムでの利用 を行っても構いません。 10 | A-4. 変更したものや部分的に使用したものは、あなたのものになります。 11 | 公開する場合は、あなたの名前の下で行って下さい。 12 | 13 | B. このソフトを利用することによって生じた損害等について、作者は 14 | 責任を負わないものとします。各自の責任においてご利用下さい。 15 | 16 | C. 著作者人格権は ○○○○ に帰属します。著作権は放棄します。 17 | 18 | D. 以上の3項は、ソース・実行バイナリの双方に適用されます。 19 | 20 | 21 | 22 | NYSL Version 0.9982 (en) (Unofficial) 23 | ---------------------------------------- 24 | A. This software is "Everyone'sWare". It means: 25 | Anybody who has this software can use it as if he/she is 26 | the author. 27 | 28 | A-1. Freeware. No fee is required. 29 | A-2. You can freely redistribute this software. 30 | A-3. You can freely modify this software. And the source 31 | may be used in any software with no limitation. 32 | A-4. When you release a modified version to public, you 33 | must publish it with your name. 34 | 35 | B. The author is not responsible for any kind of damages or loss 36 | while using or misusing this software, which is distributed 37 | "AS IS". No warranty of any kind is expressed or implied. 38 | You use AT YOUR OWN RISK. 39 | 40 | C. Copyrighted to (.......) 41 | 42 | D. Above three clauses are applied both to source and binary 43 | form of this software. 44 | -------------------------------------------------------------------------------- /README.mkd: -------------------------------------------------------------------------------- 1 | Google+ Commander 2 | ================= 3 | 4 | What is this 5 | ------------ 6 | 7 | This is userscript for Google+. This script provide shortcut keys to do following. 8 | 9 | * c to comment 10 | * s to share 11 | * e clip to Evernote (currently disabled) 12 | * m to mute 13 | * + to +1 14 | * * expand comments 15 | * / to find user 16 | * gg to go to top. 17 | * G to go to bottom. 18 | * n to show notification panel. 19 | * i to focus to a form of new entry. 20 | * esc to close current active textbox. 21 | * gh to go to Home. 22 | * gP to go to Photos. 23 | * gp to go to Profile. 24 | * gc to go to Circles. 25 | * gna to go to Notifications(all). 26 | * gnc to go to Notifications(circle). 27 | * gno to go to Notifications(otherposts). 28 | * gnp to go to Notifications(myposts). 29 | * gnm to go to Notifications(mentions). 30 | * gnP to go to Notifications(phototags). 31 | 32 | Install 33 | ------- 34 | 35 | - https://github.com/mattn/GooglePlusCommander/raw/master/googlepluscommander.user.js 36 | 37 | Author 38 | ------ 39 | 40 | - suVene 41 | - mattn 42 | 43 | Other 44 | ----- 45 | 46 | * (Test)insert a evernote affiliate code 47 | 48 | -------------------------------------------------------------------------------- /googlepluscommander.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name Google+ Commander 3 | // @author mattn 4 | // @version 0.3.5 5 | // @namespace https://github.com/mattn/googlepluscommander 6 | // @description keybinds for Google+. you can use j/k to scroll, and type 'c' to comment, 's' to share, '+' to +1. 7 | // @include https://plus.google.com/* 8 | // @match https://plus.google.com/* 9 | // ==/UserScript== 10 | 11 | (function() { 12 | function invokeMouseEvent(elem, evname) { 13 | if (!elem) { return; } 14 | var e = document.createEvent('MouseEvents'); 15 | e.initMouseEvent(evname, true, true, e.view||window, 0, 0, 0, 0, 0, 16 | false, false, false, false, 0, null); 17 | elem.dispatchEvent(e); 18 | } 19 | 20 | function click(elem) { 21 | invokeMouseEvent(elem, 'click'); 22 | } 23 | 24 | function mousedown(elem) { 25 | invokeMouseEvent(elem, 'mousedown'); 26 | } 27 | 28 | function mouseup(elem) { 29 | invokeMouseEvent(elem, 'mouseup'); 30 | } 31 | 32 | function plus(elem) { 33 | var elems = elem.getElementsByTagName('button'); 34 | for (var n = 0; n < elems.length; n++) { 35 | if (elems[n].getAttribute('title').match(/\+1/)) { 36 | click(elems[n]); 37 | break; 38 | } 39 | } 40 | } 41 | 42 | function tools(elem) { 43 | elem = getElementsByTagAndClassName('div', 'dl')[0]; 44 | return getElementsByAttribute("span", "role", "button", elem); 45 | } 46 | 47 | function getPermalinks(elem) { 48 | var ret = []; 49 | var elems = elem.getElementsByTagName('a'); 50 | for (var n = 0; n < elems.length; n++) { 51 | if (elems[n].getAttribute('href').match('/posts/')) { 52 | ret.push(elems[n]); 53 | } 54 | // how gets the child permalinks ? 55 | } 56 | return ret; 57 | } 58 | 59 | function newEntry() { 60 | var elems = getElementsByTagAndClassName('div', 'f-xe'); 61 | if (elems.length > 0) { 62 | window.scrollTo(0, 0); 63 | click(elems[0]); 64 | return true; 65 | } 66 | return false; 67 | } 68 | 69 | function closeForm(elem) { 70 | if (elem.id.match(/\.f$/)) { 71 | var cancel = findCancelElement(elem); 72 | if (cancel) { 73 | elem.blur(); 74 | cancel.focus(); 75 | if (cancel.id.match(/\.c$/)) { 76 | click(cancel); 77 | } else { 78 | mousedown(cancel); 79 | mouseup(cancel); 80 | } 81 | return true; 82 | } 83 | } 84 | return false; 85 | } 86 | 87 | function getItems() { 88 | var items = []; 89 | var elems = document.getElementsByTagName('div'); 90 | for (var n = 0; n < elems.length; n++) { 91 | var e = elems[n]; 92 | if (e.id.substring(0, 7) == 'update-') { 93 | items.push(e); 94 | } 95 | } 96 | return items; 97 | } 98 | 99 | function getOffset(elem){ 100 | var y = 0; 101 | while(elem.parentNode){ 102 | y += elem.offsetTop; 103 | elem = elem.parentNode; 104 | } 105 | return y; 106 | } 107 | 108 | var globalKeymap = { 109 | 'gg': function(e) { 110 | var elems = getItems(); 111 | var elem = elems[0]; 112 | window.scrollTo(0, getOffset(elem) - 100); 113 | click(elem); 114 | return true; 115 | }, 116 | 'G': function(e) { 117 | var elems = getItems(); 118 | var elem = elems[elems.length-1]; 119 | window.scrollTo(0, getOffset(elem) - 100); 120 | click(elem); 121 | return true; 122 | }, 123 | 'n': function(e) { 124 | click(document.getElementById('gbi1')); 125 | return true; 126 | }, 127 | 'gh': function(e) { 128 | location.href = 'https://plus.google.com/'; 129 | return true; 130 | }, 131 | 'gP': function(e) { 132 | location.href = 'https://plus.google.com/photos'; 133 | return true; 134 | }, 135 | 'gp': function(e) { 136 | location.href = 'https://plus.google.com/me'; 137 | return true; 138 | }, 139 | 'gc': function(e) { 140 | location.href = 'https://plus.google.com/circles'; 141 | return true; 142 | }, 143 | '/': function(e) { 144 | window.scrollTo(0, 0); 145 | document.getElementById('oz-search-box').focus(); 146 | }, 147 | 'gna': function(e) { 148 | location.href = 'https://plus.google.com/notifications/all'; 149 | return true; 150 | }, 151 | 'gnc': function(e) { 152 | location.href = 'https://plus.google.com/notifications/circle'; 153 | return true; 154 | }, 155 | 'gno': function(e) { 156 | location.href = 'https://plus.google.com/notifications/otherposts'; 157 | return true; 158 | }, 159 | 'gnm': function(e) { 160 | location.href = 'https://plus.google.com/notifications/myposts'; 161 | return true; 162 | }, 163 | 'gnM': function(e) { 164 | location.href = 'https://plus.google.com/notifications/mentions'; 165 | return true; 166 | }, 167 | 'gnP': function(e) { 168 | location.href = 'https://plus.google.com/notifications/phototags'; 169 | return true; 170 | } 171 | }; 172 | 173 | var itemKeymap = { 174 | 'i': function(e) { 175 | if (newEntry()) { 176 | return true; 177 | } 178 | return false; 179 | }, 180 | 'c': function(e) { 181 | if (!e.ctrlKey) { 182 | click(tools(e.target)[0]); 183 | return true; 184 | } 185 | return false; 186 | }, 187 | 'n': function(e) { 188 | click(document.getElementById('gbi1')); 189 | return true; 190 | }, 191 | 's': function(e) { 192 | click(tools(e.target)[1]); 193 | return true; 194 | }, 195 | 'm': function(e) { 196 | click(getElementsByAttribute("span", "role", "button", e.target)[0]); 197 | var menus = getElementsByAttribute("div", "role", "menuitem", e.target); 198 | var mute = menus[menus.length-2]; 199 | mousedown(mute); 200 | mouseup(mute); 201 | }, 202 | 'l': function(e) { 203 | plus(e.target); 204 | return true; 205 | }, 206 | '+': function(e) { 207 | plus(e.target); 208 | return true; 209 | }, 210 | '*': function(e) { 211 | click(getElementsByTagAndClassName('span', 'px', e.target)[0]); 212 | return true; 213 | } 214 | //'e': function(e) { 215 | // click(tools(e.target)[2]); 216 | // return true; 217 | //} 218 | }; 219 | for (var k in globalKeymap) { 220 | if (globalKeymap.hasOwnProperty(k)) { 221 | itemKeymap[k] = globalKeymap[k]; 222 | } 223 | } 224 | 225 | var stack = ""; 226 | var timer = 0; 227 | function handleKeys(e, m) { 228 | var c = String.fromCharCode(e.which ? e.which : 229 | e.keyCode ? e.keyCode : e.charCode); 230 | stack += c; 231 | var u = 0; 232 | for (var k in m) { 233 | if (k == stack) { 234 | e.preventDefault(); 235 | var f = m[stack]; 236 | if (f) { 237 | stack = ""; 238 | if (f(e)) { 239 | return true; 240 | } 241 | } 242 | return false; 243 | } else if (k.substring(0, stack.length) == stack) { 244 | u++; 245 | } 246 | } 247 | try { clearTimeout(timer); } catch(ee) {} 248 | if (u) { 249 | e.preventDefault(); 250 | timer = setTimeout(function() { 251 | var f = m[stack]; 252 | stack = ""; 253 | if (f) { 254 | f(e); 255 | } 256 | }, 2000); 257 | } else { 258 | stack = ""; 259 | } 260 | } 261 | 262 | function installGlobalKeys(elem) { 263 | elem.addEventListener('keypress', function(e) { 264 | if (e.target.id.substring(0, 7) == 'update-') { return; } 265 | if (e.target.nodeName.toLowerCase() == 'input') { return; } 266 | if (e.target.getAttribute('g_editable') == 'true') { return; } 267 | return handleKeys(e, globalKeymap); 268 | }, false); 269 | } 270 | 271 | function installItemKeys(elem) { 272 | elem.addEventListener('keypress', function(e) { 273 | if (e.target.id.substring(0, 7) != 'update-') { return; } 274 | return handleKeys(e, itemKeymap); 275 | }, false); 276 | elem.className += ' gpcommander'; 277 | } 278 | 279 | function installEditorKeys(elem) { 280 | elem.addEventListener('keyup', function(e) { 281 | var hooked = false; 282 | if (hasClass(e.target, 'editable') && e.target.innerHTML.replace(/<[^>]+>/g, '').length === 0) { 283 | var c = String.fromCharCode(e.keyCode ? e.keyCode : e.charCode); 284 | if (!e.shiftKey) { 285 | c = c.toLowerCase(); 286 | } 287 | if (c === '\x1b') { 288 | hooked = closeForm(e.target); 289 | } 290 | } 291 | if (!hooked) { 292 | e.preventDefault(); 293 | } 294 | }, false); 295 | elem.className += ' gpcommander'; 296 | } 297 | 298 | function hasClass(elem, clazz) { 299 | var zz = elem.className.split(/\s+/g); 300 | for (var m = 0; m < zz.length; m++) { 301 | if (zz[m] == clazz) { return true; } 302 | } 303 | return false; 304 | } 305 | 306 | function getElementsByAttribute(tag, attr, value, node) { 307 | var retval = []; 308 | var elems = (node || document).getElementsByTagName(tag); 309 | for (var i = 0, I = elems.length; i < I; ++i) { 310 | var e = elems[i]; 311 | if (e.getAttribute(attr) == value) { 312 | retval.push(e); 313 | } 314 | } 315 | return retval; 316 | } 317 | 318 | function getElementsByTagAndClassName(tag, clazz, node) { 319 | var retval = []; 320 | var elems = (node || document).getElementsByTagName(tag); 321 | for (var i = 0, I = elems.length; i < I; ++i) { 322 | var e = elems[i]; 323 | if (hasClass(e, clazz)) { 324 | retval.push(e); 325 | } 326 | } 327 | return retval; 328 | } 329 | 330 | function findElement(elem, matcher) { 331 | if (matcher(elem)) { 332 | return elem; 333 | } 334 | for (var child = elem.firstChild; child; child = child.nextSibling) { 335 | var found = findElement(child, matcher); 336 | if (found) { 337 | return found; 338 | } 339 | } 340 | return undefined; 341 | } 342 | 343 | function findElementFromNext(elem, matcher) { 344 | for (var curr = elem.nextSibling; curr; curr = curr.nextSibling) { 345 | var found = findElement(curr, matcher); 346 | if (found) { 347 | return found; 348 | } 349 | } 350 | if (elem.parentNode) { 351 | return findElementFromNext(elem.parentNode, matcher); 352 | } else { 353 | return undefined; 354 | } 355 | } 356 | 357 | function findCancelElement(elem) { 358 | return findElementFromNext(elem, function(e) { 359 | return e.id && e.id.match(/\.c(ancel)?$/); 360 | }); 361 | } 362 | 363 | function installEvernoteClip(elem) { 364 | var ns = tools(elem); 365 | var url = getPermalinks(elem); 366 | var ln = ns[ns.length - 1]; 367 | var n = document.createTextNode(' - '); 368 | ln.parentNode.appendChild(n); 369 | // n = document.createElement('span'); 370 | // n.appendChild(document.createTextNode('ClipEver')); 371 | n = document.createElement('img'); 372 | n.setAttribute('src', 'http://static.evernote.com/article-clipper.png'); 373 | n.setAttribute('class', 'd-h'); 374 | n.setAttribute('role', 'button'); 375 | n.setAttribute('tabindex', '0'); 376 | n.setAttribute('onclick', "Evernote.doClip({styling: 'full', url: '" + url + "', code: '____2531', contentId: '" + elem.id + "'})"); 377 | ln.parentNode.appendChild(n); 378 | } 379 | 380 | function installExternalScripts() { 381 | var n = document.createElement('script'); 382 | n.setAttribute('type', 'text/javascript'); 383 | n.setAttribute('src', 'http://static.evernote.com/noteit.js'); 384 | document.getElementsByTagName('head')[0].appendChild(n); 385 | } 386 | 387 | function install() { 388 | var elems = document.getElementsByTagName('div'); 389 | for (var n = 0; n < elems.length; n++) { 390 | var e = elems[n]; 391 | if (e.id.substring(0, 7) == 'update-' && !hasClass(e, 'gpcommander')) { 392 | installItemKeys(e); 393 | //installEvernoteClip(e); 394 | } else if (hasClass(e, 'editable') && !hasClass(e, 'gpcommander')) { 395 | installEditorKeys(e); 396 | } 397 | } 398 | } 399 | window.setInterval(install, 1000); 400 | installGlobalKeys(document.body); 401 | //installExternalScripts(); 402 | })(); 403 | --------------------------------------------------------------------------------