Feedly Scroll Marker

Display the marker of page scroll in Feedly. / Feedlyでページスクロールしたときにマーカーを表示します。

  1. // ==UserScript==
  2. // @name Feedly Scroll Marker
  3. // @description Display the marker of page scroll in Feedly. / Feedlyでページスクロールしたときにマーカーを表示します。
  4. // @id FeedlyScrollMarker
  5. // @namespace http://userscripts.org/scripts/show/174679
  6. // @homepage https://greasyfork.org/scripts/899-feedly-scroll-marker
  7. // @include http://feedly.com/*
  8. // @include https://feedly.com/*
  9. // @grant GM_addStyle
  10. // @version 1.03
  11. // ==/UserScript==
  12.  
  13. (function() {
  14.  
  15. try { if (typeof localStorage !== 'object') return alert('FSM Error: DOM Storage'); }
  16. catch(er) { return alert('FSM Error: DOM Storage'); }
  17. var $id = function(id) { return document.getElementById(id); }, iInit;
  18.  
  19. var init = function() {
  20. var nScrPos = 0, nTime = 750, shift = false, iMarker, st;
  21. var ism = document.createElement('div');
  22. ism.id = 'fsm_scroll_marker';
  23. document.body.appendChild(ism);
  24. var CSS = [
  25. '#fsm_settings { display: none; color: black; position: fixed; top: 68px; left: 68px; z-index: 90200; background: rgba(255, 255, 255, 0.98); border: 1px solid #999999; border-radius: 4px; min-width: 35em; box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2); }',
  26. '#fsm_s_titlebar { background-color: #666666; border-radius: 4px 4px 0 0; padding: 2px 0 0 4px; height: 2em; }',
  27. '#fsm_s_title a { font-size: 110%; font-weight: bold; color: white; text-decoration: none; }',
  28. '#fsm_s_title a:hover { color: #FFFF99; }',
  29. '#fsm_s_btn { position: absolute; top: 2px; right: 4px; }',
  30. '#fsm_s_ok { margin-right: 0.5em; padding: 0 2em; }',
  31. '#fsm_s_cancel { padding: 0 1ex; }',
  32. '#fsm_s_ok, #fsm_s_cancel { font-size: 80%; }',
  33. '#fsm_s_body { padding: 0.5em 1em; }',
  34. '#fsm_s_body fieldset { margin: 4px 2px; background: rgba(255, 255, 255, 0); }',
  35. '#fsm_s_body input { margin-left: 0.5em; text-align: center; width: 6ex; }',
  36. '#fsm_s_body input[type="checkbox"] { vertical-align: middle; width: auto; }',
  37. '#fsm_s_time { vertical-align: inherit; }',
  38. '#fsm_scroll_marker, #fsm_scroll_marker2 { position: absolute; width: 100%; height: 4px; margin-top: -2px; background: rgba(255,0,0,0.25); display: none; }',
  39. '#fsm_scroll_marker { z-index: 88801; }',
  40. '#fsm_scroll_marker2 { z-index: 88802; }'
  41. ].join('');
  42. GM_addStyle(CSS);
  43. var locale_ja = [
  44. '設定',
  45. 'キャンセル',
  46. 'マーカーを表示する時間 (ミリ秒):',
  47. 'マーカーを表示するショートカットキー'
  48. ];
  49. var locale_en = [
  50. 'Settings',
  51. 'Cancel',
  52. 'Time to display the marker (msec):',
  53. 'Keyboard shortcut to display the marker'
  54. ];
  55. var loc = (window.navigator.language === 'ja') ? locale_ja : locale_en;
  56. var currentMode = function() {
  57. var e1 = $id('feedlyPageFX'), e2 = $id('timeline'),
  58. t1 = 'presentation-', t2 = 'EntryList';
  59. if (document.getElementsByClassName('floatingEntryScroller').length) return 'fes';
  60. if (e1 && e1.hasChildNodes()) {
  61. e1 = e1.firstChild;
  62. return (e1.classList.contains(t1 + '0')) ? 'T'
  63. : (e1.classList.contains(t1 + '4')) ? 'M2'
  64. : (e1.classList.contains(t1 + '5')) ? 'C'
  65. : (e1.classList.contains(t1 + '100')) ? 'F' : '';
  66. }
  67. if (e2) {
  68. return (e2.classList.contains('u0' + t2)) ? 'T'
  69. : (e2.classList.contains('u4' + t2)) ? 'M1'
  70. : (e2.classList.contains('u5' + t2)) ? 'C'
  71. : (e2.classList.contains('u100' + t2)) ? 'F' : '';
  72. }
  73. return '';
  74. };
  75. var currentEntry = function(m) {
  76. var mode = (m) ? m : currentMode();
  77. switch (mode) {
  78. case 'T': case 'M1':
  79. return document.querySelector('#timeline .inlineFrame');
  80. case 'C': case 'M2': case 'fes':
  81. return document.querySelector('#floatingEntry .floatingEntryScroller');
  82. case 'F':
  83. return document.querySelector('#timeline .selectedEntry');
  84. default:
  85. return document.querySelector('#mainArea .inlineFrame');
  86. }
  87. };
  88. var setMarker = function() {
  89. var mode = currentMode();
  90. if (mode === 'C' || mode === 'M2' || mode === 'fes') {
  91. if ($id('fsm_scroll_marker2')) ism = $id('fsm_scroll_marker2');
  92. else {
  93. var ism2 = document.createElement('div');
  94. ism2.id = 'fsm_scroll_marker2';
  95. currentEntry(mode).appendChild(ism2);
  96. ism = ism2;
  97. }
  98. } else ism = $id('fsm_scroll_marker');
  99. };
  100. var viewSettings = function() {
  101. if ($id('fsm_settings').style.display === 'block') {
  102. $id('fsm_settings').style.display = 'none';
  103. return;
  104. }
  105. $id('fsm_s_time').value = st.time;
  106. $id('fsm_s_key_page').checked = (st.key_page) ? true : false;
  107. $id('fsm_s_key_space').checked = (st.key_space) ? true : false;
  108. $id('fsm_settings').style.display = 'block';
  109. };
  110. var loadSettings = function() {
  111. st = {};
  112. try {
  113. st = JSON.parse(localStorage.getItem('FeedlyScrollMarker_settings')) || {};
  114. } catch(er) { alert('FSM Error: Load Settings'); }
  115. var notB = function(a) {
  116. return (typeof a !== 'boolean') ? true : false;
  117. };
  118. var notN = function(a) {
  119. return (typeof a !== 'number') ? true : false;
  120. };
  121. if (notN(st.time)) st.time = nTime;
  122. if (notB(st.key_page)) st.key_page = true;
  123. if (notB(st.key_space)) st.key_space = true;
  124. };
  125. var saveSettings = function() {
  126. try {
  127. localStorage.setItem('FeedlyScrollMarker_settings', JSON.stringify(st));
  128. } catch(er) { alert('FSM Error: Save Settings'); }
  129. };
  130. var div = document.createElement('div');
  131. div.id = 'fsm_settings';
  132. document.body.appendChild(div);
  133. $id('fsm_settings').innerHTML = '<div id="fsm_s_titlebar"><div id="fsm_s_title"><a href="https://greasyfork.org/scripts/899-feedly-scroll-marker" target="_blank">Feedly Scroll Marker ' + loc[0] + '</a></div><div id="fsm_s_btn"><input type="button" id="fsm_s_ok" value="OK"><input type="button" id="fsm_s_cancel" value="' + loc[1] + '"></div></div><div id="fsm_s_body"><label>' + loc[2] + '<input id="fsm_s_time" type="text" maxlength="4"></label><fieldset><legend>' + loc[3] + ' : </legend><label><input id="fsm_s_key_page" type="checkbox">PageUp / PageDown</label><br><label><input id="fsm_s_key_space" type="checkbox">Space / Shift+Space</label></fieldset></div>';
  134. var eTabs = $id('feedlyTabs'), eItem;
  135. if (eTabs) {
  136. eItem = document.createElement('div');
  137. eItem.className = 'tab';
  138. eItem.innerHTML = '<div class="header target"><div id="fsm_settings-menu" class="label primary iconless">Scroll Marker ' + loc[0] + '</div></div>';
  139. eTabs.appendChild(eItem);
  140. }
  141. document.addEventListener('keydown', function(e) {
  142. if (!/^input|^textarea/i.test(e.target.tagName) && st.time !== 0) {
  143. setMarker();
  144. if (
  145. (st.key_page && e.keyCode === 33) ||
  146. (st.key_page && e.keyCode === 34) ||
  147. (st.key_space && e.keyCode === 32)
  148. ) {
  149. var mode = currentMode();
  150. ism.style.display = 'none';
  151. if (mode === 'C' || mode === 'M2' || mode === 'fes') nScrPos = currentEntry(mode).scrollTop;
  152. else nScrPos = window.scrollY;
  153. if (e.shiftKey) shift = true;
  154. } else if (e.keyCode === 13) {
  155. if (currentEntry()) {
  156. window.clearTimeout(iMarker);
  157. ism.style.display = 'none';
  158. }
  159. } else if (ism.style.display !== 'none') {
  160. window.clearTimeout(iMarker);
  161. ism.style.display = 'none';
  162. }
  163. }
  164. }, true);
  165. document.addEventListener('keyup', function(e) {
  166. if (!/^input|^textarea/i.test(e.target.tagName) && st.time !== 0) {
  167. var mode = currentMode(), sch, sst, ele;
  168. if (mode === 'C' || mode === 'M2' || mode === 'fes') {
  169. ele = currentEntry(mode);
  170. sch = ele.clientHeight;
  171. sst = ele.scrollTop;
  172. } else {
  173. sch = document.body.parentNode.clientHeight;
  174. sst = document.body.parentNode.scrollTop;
  175. }
  176. if (
  177. (st.key_page && e.keyCode === 33) ||
  178. (st.key_space && e.keyCode === 32 && e.shiftKey)
  179. ) {
  180. if (nScrPos !== sst) {
  181. ism.style.display = 'block';
  182. ism.style.top = (nScrPos - (ism.scrollHeight / 2)) + 'px';
  183. window.clearTimeout(iMarker);
  184. if (st.time > 0) {
  185. iMarker = window.setTimeout(function() {
  186. window.clearTimeout(iMarker);
  187. ism.style.display = 'none';
  188. }, st.time);
  189. }
  190. }
  191. shift = false;
  192. } else if (
  193. (st.key_page && e.keyCode === 34) ||
  194. (st.key_space && e.keyCode === 32 && !e.shiftKey)
  195. ) {
  196. if (nScrPos !== sst) {
  197. ism.style.display = 'block';
  198. ism.style.top = (nScrPos + sch + (ism.scrollHeight / 2)) + 'px';
  199. window.clearTimeout(iMarker);
  200. if (st.time > 0) {
  201. iMarker = window.setTimeout(function() {
  202. window.clearTimeout(iMarker);
  203. ism.style.display = 'none';
  204. }, st.time);
  205. }
  206. }
  207. shift = false;
  208. }
  209. }
  210. }, true);
  211. document.addEventListener('click', function(e) {
  212. if (e.button >= 2) return;
  213. if (e.target.id === 'fsm_settings-menu') {
  214. viewSettings();
  215. } else if (e.target.id === 'fsm_s_ok') {
  216. var time = $id('fsm_s_time').value, problem = false;
  217. if (time === '' || /^\s+$/.test(time)) st.time = nTime;
  218. else if (time && !isNaN(time)) st.time = Number(time);
  219. else problem = true;
  220. if (!problem) {
  221. st.key_page = $id('fsm_s_key_page').checked;
  222. st.key_space = $id('fsm_s_key_space').checked;
  223. $id('fsm_s_ok').blur();
  224. $id('fsm_settings').style.display = 'none';
  225. saveSettings();
  226. }
  227. } else if (e.target.id === 'fsm_s_cancel') {
  228. $id('fsm_s_cancel').blur();
  229. $id('fsm_settings').style.display = 'none';
  230. } else if (ism.style.display !== 'none') {
  231. window.clearTimeout(iMarker);
  232. ism.style.display = 'none';
  233. }
  234. }, false);
  235. loadSettings();
  236. };
  237.  
  238. iInit = window.setInterval(function() {
  239. var e = $id('feedlyTitleBar');
  240. if (e && e.hasChildNodes()) {
  241. window.clearInterval(iInit);
  242. window.setTimeout(function() {
  243. init();
  244. }, 1000);
  245. }
  246. }, 500);
  247.  
  248. })();