For a mouseless future.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

onload.js 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. var conf = {
  2. scroll_speed: 0.3,
  3. scroll_speed_fast: 1.1,
  4. scroll_friction: 0.8,
  5. chars: "SANOTEHUCP",
  6. input_whitelist: ["checkbox", "radio", "hidden", "submit", "reset", "button", "file", "image"],
  7. location_change_check_timeout: 2000
  8. }
  9. var keys = {
  10. scroll_up: {code: "T"},
  11. scroll_down: {code: "N"},
  12. scroll_up_fast: {code: 219, shiftKey: true},
  13. scroll_down_fast: {code: 221, shiftKey: true},
  14. blobs_show: {code: "D"},
  15. blobs_show_reload: {code: "D", ctrlKey: true},
  16. blobs_hide: {code: 27},
  17. blobs_click: {code: 13},
  18. blobs_click_new_tab: {code: 13, shiftKey: true},
  19. blobs_backspace: {code: 8},
  20. elem_deselect: {code: 27},
  21. change_tab_left: {code: "H"},
  22. change_tab_right: {code: "S"},
  23. move_tab_left: {code: "H", shiftKey: true},
  24. move_tab_right: {code: "S", shiftKey: true},
  25. history_back: {code: "H", ctrlKey: true},
  26. history_forward: {code: "S", ctrlKey: true}
  27. }
  28. for (var i in keys) {
  29. if (typeof keys[i].code === "string") {
  30. keys[i].code = keys[i].code.charCodeAt(0);
  31. }
  32. }
  33. function isMatch(k, evt) {
  34. if ((k.code === evt.keyCode)
  35. && (!!k.ctrlKey == evt.ctrlKey)
  36. && (!!k.shiftKey == evt.shiftKey)
  37. && (!!k.altKey == evt.altKey)
  38. && (!!k.metaKey == evt.metaKey)) {
  39. return true;
  40. }
  41. return false;
  42. }
  43. //There's a lot we don't want to do if we're not on an actual webpage, but on
  44. //the "speed dial"-ish pages.
  45. var onWebPage = (document.body !== undefined);
  46. function randomChar() {
  47. var index = Math.floor(Math.random() * conf.chars.length);
  48. return conf.chars[index];
  49. }
  50. function getElemPos(elem) {
  51. var curtop = 0;
  52. var curleft = 0;
  53. do {
  54. curtop += elem.offsetTop;
  55. curleft += elem.offsetLeft;
  56. } while (elem = elem.offsetParent);
  57. return {top: curtop, left: curleft};
  58. }
  59. var blobList = {
  60. blobs: {},
  61. container: null,
  62. overview: null,
  63. visible: false,
  64. needLoadBlobs: true,
  65. currentKey: "",
  66. createContainer: function() {
  67. var container = document.createElement("div");
  68. container.style =
  69. "pointer-events: none;"+
  70. "display: none;"+
  71. "position: absolute;"+
  72. "top: 0px;"+
  73. "left: 0px;"+
  74. "z-index: 2147483647"; //Max z-index value in most browsers
  75. document.body.appendChild(container);
  76. blobList.container = container;
  77. },
  78. createOverview: function() {
  79. var overview = document.createElement("div");
  80. overview.style =
  81. "position: fixed;"+
  82. "width: 100%;"+
  83. "bottom: 0px;"+
  84. "background-color: white;"+
  85. "border-top: 2px solid black;"+
  86. "color: black;"+
  87. "font-size: 10pt;"+
  88. "padding: 5px;"+
  89. "height: 20px;"+
  90. "z-index: 2147483647"; //Max z-index value in most browsers
  91. blobList.container.appendChild(overview);
  92. blobList.overview = overview;
  93. },
  94. init: function() {
  95. if (!onWebPage)
  96. return;
  97. blobList.createContainer();
  98. window.addEventListener("scroll", function() {
  99. blobList.needLoadBlobs = true;
  100. });
  101. },
  102. currentIndex: 0,
  103. loadBlobs: function() {
  104. if (!onWebPage)
  105. return;
  106. var linkElems = document.querySelectorAll("a, button, input, textarea");
  107. //Remove old container contents
  108. blobList.container.innerHTML = "";
  109. blobList.createOverview();
  110. //Remove old blobs
  111. blobList.blobs = {};
  112. var i = 0;
  113. function addBlob() {
  114. var linkElem = linkElems[i];
  115. i += 1;
  116. if (i > linkElems.length)
  117. return false;
  118. if (linkElem === undefined)
  119. return true;
  120. //We don't want hidden elements
  121. if ((linkElem === undefined)
  122. || (linkElem.style.display == "none")
  123. || (linkElem.style.visibility == "hidden")) {
  124. return true;
  125. }
  126. //Get element's absolute position
  127. var pos = getElemPos(linkElem);
  128. //Lots of things which don't really exist have an X and Y value of 0
  129. if (pos.top == 0 && pos.left == 0)
  130. return true;
  131. //We don't need to get things far above our current scroll position
  132. if (pos.top < (window.scrollY - 100))
  133. return true;
  134. //We don't need things below our scroll position either
  135. if (pos.top - 100 > (window.scrollY + window.innerHeight))
  136. return true;
  137. var key = randomChar();
  138. while (blobList.blobs[key])
  139. key += randomChar();
  140. var blobElem = document.createElement("div");
  141. blobElem.innerHTML = key;
  142. blobElem.style = [
  143. "position: absolute",
  144. "background-color: yellow",
  145. "border: 1px solid black",
  146. "border-radius: 10px",
  147. "padding-left: 3px",
  148. "padding-right: 3px",
  149. "color: black",
  150. "top: "+pos.top+"px",
  151. "left: "+pos.left+"px",
  152. "line-height: 12px",
  153. "font-size: 12px"
  154. ].join(" !important;");
  155. blobList.container.appendChild(blobElem);
  156. blobList.blobs[key] = {
  157. blobElem: blobElem,
  158. linkElem: linkElem
  159. }
  160. return true;
  161. }
  162. while (addBlob()) {};
  163. },
  164. showBlobs: function() {
  165. blobList.visible = true;
  166. blobList.container.style.display = "block";
  167. },
  168. hideBlobs: function() {
  169. blobList.currentKey = "";
  170. blobList.visible = false;
  171. blobList.container.style.display = "none";
  172. },
  173. click: function() {
  174. if (!blobList.visible)
  175. return;
  176. var blob = blobList.blobs[blobList.currentKey];
  177. if (!blob)
  178. return;
  179. blob.linkElem.click();
  180. blob.linkElem.focus();
  181. blobList.hideBlobs();
  182. },
  183. clickNewTab: function() {
  184. if (!blobList.visible)
  185. return;
  186. var blob = blobList.blobs[blobList.currentKey];
  187. if (!blob)
  188. return;
  189. if (blob.linkElem.tagName == "A" && blob.linkElem.href) {
  190. self.port.emit("tab_open", blob.linkElem.href);
  191. } else {
  192. blob.linkElem.click();
  193. blob.linkElem.focus();
  194. }
  195. blobList.hideBlobs();
  196. },
  197. appendKey: function(c) {
  198. blobList.currentKey += c;
  199. blobList.overview.innerHTML = blobList.currentKey;
  200. },
  201. backspace: function() {
  202. blobList.currentKey = blobList.currentKey.substring(0, blobList.currentKey.length - 1);
  203. blobList.overview.innerHTML = blobList.currentKey;
  204. }
  205. }
  206. blobList.init();
  207. //Reload blobs whenever the URL changes
  208. var currentUrl = location.href;
  209. setInterval(function() {
  210. if (currentUrl !== location.href) {
  211. blobList.loadBlobs();
  212. }
  213. currentUrl = location.href;
  214. }, conf.location_change_check_timeout);
  215. var pressedKeys = [];
  216. function inArray(arr, val) {
  217. return (arr.indexOf(val) !== -1);
  218. }
  219. function isValidElem(elem) {
  220. var tag = elem.tagName.toLowerCase();
  221. if (tag === "textarea")
  222. return false;
  223. if (tag === "select")
  224. return false;
  225. if (elem.contentEditable.toLowerCase() === "true")
  226. return false;
  227. if ((tag === "input")
  228. && (!inArray(conf.input_whitelist, elem.type.toLowerCase()))) {
  229. return false;
  230. }
  231. return true;
  232. }
  233. window.addEventListener("keydown", function(evt) {
  234. if (/about:.+/.test(location.href))
  235. return;
  236. var active = document.activeElement;
  237. //We don't want to do anything if the user is tpying in an input field,
  238. //unless the key is to deselect an input field
  239. if (!isValidElem(active)) {
  240. if (isMatch(keys.elem_deselect, evt)) {
  241. active.blur();
  242. blobList.hideBlobs();
  243. return;
  244. } else {
  245. return;
  246. }
  247. }
  248. //User is typing a key to a blob
  249. if (blobList.visible) {
  250. //Hide blobs if appropriate
  251. if (isMatch(keys.blobs_hide, evt)) {
  252. blobList.hideBlobs();
  253. return;
  254. }
  255. //Backspace if appropriate
  256. if (isMatch(keys.blobs_backspace, evt)) {
  257. blobList.backspace();
  258. return;
  259. }
  260. var c = String.fromCharCode(evt.keyCode);
  261. if (conf.chars.indexOf(c) !== -1) {
  262. blobList.appendKey(c);
  263. evt.preventDefault();
  264. evt.stopPropagation();
  265. return false;
  266. }
  267. }
  268. //Handle other key presses
  269. //Deselect element
  270. if (onWebPage && isMatch(keys.elem_deselect, evt)) {
  271. blobList.hideBlobs();
  272. active.blur();
  273. //Show/hide/reload blobs
  274. } else if (onWebPage && !blobList.visible && isMatch(keys.blobs_show, evt)) {
  275. if (blobList.needLoadBlobs)
  276. blobList.loadBlobs();
  277. blobList.needLoadBlobs = false;
  278. blobList.showBlobs();
  279. } else if (onWebPage && !blobList.visible && isMatch(keys.blobs_show_reload, evt)) {
  280. blobList.loadBlobs();
  281. blobList.needLoadBlobs = false;
  282. blobList.showBlobs();
  283. } else if (onWebPage && blobList.visible && isMatch(keys.blobs_hide, evt)) {
  284. blobList.hideBlobs();
  285. //Simulate clicks
  286. } else if (onWebPage && blobList.visible && isMatch(keys.blobs_click, evt)) {
  287. blobList.click();
  288. } else if (onWebPage && blobList.visible && isMatch(keys.blobs_click_new_tab, evt)) {
  289. blobList.clickNewTab();
  290. //Scrolling
  291. } else if (onWebPage && isMatch(keys.scroll_up, evt)) {
  292. scroll.start(-conf.scroll_speed);
  293. } else if (onWebPage && isMatch(keys.scroll_down, evt)) {
  294. scroll.start(conf.scroll_speed);
  295. } else if (onWebPage && isMatch(keys.scroll_up_fast, evt)) {
  296. scroll.start(-conf.scroll_speed_fast);
  297. } else if (onWebPage && isMatch(keys.scroll_down_fast, evt)) {
  298. scroll.start(conf.scroll_speed_fast);
  299. //Back and forwards
  300. } else if (isMatch(keys.history_back, evt)) {
  301. history.back();
  302. } else if (isMatch(keys.history_forward, evt)) {
  303. history.forward();
  304. //Change tab
  305. } else if (isMatch(keys.change_tab_left, evt)) {
  306. self.port.emit("change_tab_left");
  307. } else if (isMatch(keys.change_tab_right, evt)) {
  308. self.port.emit("change_tab_right");
  309. //Move tab
  310. } else if (isMatch(keys.move_tab_left, evt)) {
  311. self.port.emit("move_tab_left");
  312. } else if (isMatch(keys.move_tab_right, evt)) {
  313. self.port.emit("move_tab_right");
  314. //We don't want to stop the event from propagating
  315. //if it hasn't matched anything yet
  316. } else {
  317. return true;
  318. }
  319. evt.preventDefault();
  320. evt.stopPropagation();
  321. return false;
  322. }, true);
  323. window.addEventListener("keyup", function(evt) {
  324. if ((isMatch(keys.scroll_up, evt))
  325. || (isMatch(keys.scroll_down, evt))
  326. || (isMatch(keys.scroll_up_fast, evt))
  327. || (isMatch(keys.scroll_down_fast, evt))) {
  328. scroll.stop();
  329. }
  330. }, true);
  331. var scroll = {
  332. start: function(acceleration) {
  333. scroll.acceleration = acceleration;
  334. if (scroll.raf == null)
  335. scroll.update();
  336. },
  337. stop: function() {
  338. scroll.acceleration = 0;
  339. },
  340. update: function() {
  341. var tdiff = scroll.endTime - scroll.startTime;
  342. if (tdiff < 100) {
  343. scroll.velocity += scroll.acceleration;
  344. window.scrollBy(0, scroll.velocity * tdiff);
  345. scroll.velocity *= conf.scroll_friction;
  346. }
  347. if (tdiff < 100 && scroll.velocity > -0.1 && scroll.velocity < 0.1) {
  348. scroll.velocity = 0;
  349. cancelAnimationFrame(scroll.raf);
  350. scroll.raf = null;
  351. } else {
  352. scroll.startTime = scroll.endTime;
  353. scroll.endTime = new Date().getTime();
  354. scroll.raf = requestAnimationFrame(scroll.update);
  355. }
  356. },
  357. raf: null,
  358. acceleration: 0,
  359. velocity: 0,
  360. startDate: 0,
  361. endDate: 0
  362. }