For a mouseless future.
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

onload.js 9.3KB

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