For a mouseless future.
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

onload.js 8.5KB

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