InoReader Scroll Marker

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

  1. // ==UserScript==
  2. // @name InoReader Scroll Marker
  3. // @description Display the marker of page scroll in InoReader. / InoReaderでページスクロールしたときにマーカーを表示します。
  4. // @description:en Display the marker of page scroll in InoReader.
  5. // @description:ja InoReaderでページスクロールしたときにマーカーを表示します。
  6. // @id InoReaderScrollMarker
  7. // @namespace http://userscripts.org/scripts/show/173921
  8. // @homepage https://greasyfork.org/scripts/894-inoreader-scroll-marker
  9. // @include http://inoreader.com/*
  10. // @include https://inoreader.com/*
  11. // @include http://www.inoreader.com/*
  12. // @include https://www.inoreader.com/*
  13. // @include http://jp.inoreader.com/*
  14. // @include https://jp.inoreader.com/*
  15. // @include http://us.inoreader.com/*
  16. // @include https://us.inoreader.com/*
  17. // @exclude *inoreader.com/stream*
  18. // @exclude *inoreader.com/m/*
  19. // @version 1.13
  20. // @grant GM_addStyle
  21. // ==/UserScript==
  22.  
  23. (function() {
  24. 'use strict';
  25.  
  26. if (!/Ino\s?Reader/i.test(document.title)) return;
  27. try {
  28. if (typeof localStorage !== 'object') return window.alert('IRSM Error: DOM Storage');
  29. } catch (er) {
  30. return window.alert('IRSM Error: DOM Storage');
  31. }
  32. var $id = function(id) {
  33. return document.getElementById(id);
  34. }, iInit;
  35.  
  36. var init = function() {
  37. var ismRp = document.createElement('div'),
  38. ismTwc = document.createElement('div'),
  39. nScrPos = 0,
  40. nCheck = 0,
  41. nTime = 750,
  42. shift = false,
  43. exUpKey, exUpCode, exUpShift, exDownKey, exDownCode, exDownShift,
  44. exOpenKey, exOpenCode, exOpenShift, exSpaceKey, exSpaceCode, exSpaceShift,
  45. exSSpaceKey, exSSpaceCode, exSSpaceShift, articleID, iMarker, iIrkc, st;
  46. ismRp.id = 'irsm_scroll_marker_rp';
  47. ismTwc.id = 'irsm_scroll_marker_twc';
  48. ismRp.className = 'irsm_scroll_marker';
  49. ismTwc.className = 'irsm_scroll_marker';
  50. $id('reader_pane').insertBefore(ismRp, $id('reader_pane').firstChild);
  51. new MutationObserver(function() {
  52. var rp = $id('reader_pane');
  53. if (!$id('irsm_scroll_marker_rp') && rp && rp.childNodes.length) {
  54. rp.insertBefore(ismRp, rp.firstChild);
  55. }
  56. }).observe($id('reader_pane'), {
  57. childList: true
  58. });
  59. new MutationObserver(function() {
  60. var twcafc = document.querySelector('#three_way_contents > .article_full_contents:last-child');
  61. if (!$id('irsm_scroll_marker_twc') && twcafc && twcafc.childNodes.length) {
  62. twcafc.insertBefore(ismTwc, twcafc.firstChild);
  63. }
  64. }).observe($id('three_way_contents'), {
  65. childList: true
  66. });
  67. var CSS = '#irsm_settings { display: none; color: black; position: absolute; top: 68px; left: 68px; z-index: 90200; background: rgba(255, 255, 255, 0.98); border: 1px solid #999999; border-radius: 4px; box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2); -moz-user-select: none; -webkit-user-select: none; }' +
  68. '#irsm_s_titlebar { background-color: #666666; border-radius: 4px 4px 0 0; padding: 2px 0 0 4px; height: 2em; min-width: 20em; }' +
  69. '#irsm_s_title a { font-size: 110%; font-weight: bold; color: white; text-decoration: none; }' +
  70. '#irsm_s_title a:hover { color: #FFFF99; }' +
  71. '#irsm_s_btn { position: absolute; top: 2px; right: 4px; }' +
  72. '#irsm_s_ok { margin-right: 0.5em; padding: 0 2em; }' +
  73. '#irsm_s_cancel { padding: 0 1ex; }' +
  74. '#irsm_s_ok, #irsm_s_cancel { font-size: 80%; }' +
  75. '#irsm_s_body { padding: 0.5em 1em; min-width: 30em; }' +
  76. '#irsm_s_body fieldset { margin: 4px 2px; background: rgba(255, 255, 255, 0); }' +
  77. '#irsm_s_body input { margin-left: 0.5em; text-align: center; width: 6ex; }' +
  78. '#irsm_s_body input[type="checkbox"] { vertical-align: middle; width: auto; }' +
  79. '#irsm_s_time { vertical-align: inherit; }' +
  80. '.irsm_scroll_marker { position: absolute; left: 0; width: 100%; height: 4px; margin-top: -2px; background: rgba(255,0,0,0.25); display: none; z-index: 9000; }' +
  81. '.irsm_s_hide { display: none; }' +
  82. '#three_way_contents .article_full_contents { padding: 4px 0; }' +
  83. '#three_way_contents .article_footer { display: none; box-shadow: 0 2px 4px 0 #333; width: auto; padding-left: 20px; padding-right: 20px; }' +
  84. '#subscriptions_buttons:hover + #splitter #three_way_contents .article_footer, #three_way_contents .article_footer:hover { display: block; }';
  85. GM_addStyle(CSS);
  86.  
  87. var locale_ja = [
  88. '設定',
  89. 'キャンセル',
  90. 'マーカーを表示する時間 (ミリ秒):',
  91. 'マーカーを表示するショートカットキー'
  92. ];
  93. var locale_en = [
  94. 'Settings',
  95. 'Cancel',
  96. 'Time to display the marker (msec):',
  97. 'Keyboard shortcut to display the marker'
  98. ];
  99. var loc = (window.navigator.language === 'ja') ? locale_ja : locale_en;
  100.  
  101. var currentEntry = function() {
  102. if ($id('article_dialog')) {
  103. return document.querySelector('#article_dialog > .article_full_contents');
  104. }
  105. if ($id('three_way_contents') && $id('three_way_contents').style.display !== 'none' && $id('reader_pane').getElementsByClassName('article_current article_current_3way')[0]) {
  106. return document.querySelector('#three_way_contents > .article_full_contents:last-child');
  107. }
  108. if ($id('subscriptions_articles')) {
  109. return document.querySelector('#subscriptions_articles > .article_current');
  110. }
  111. return document.querySelector('#reader_pane .article_current');
  112. };
  113.  
  114. var checkArticle = function(e, key) {
  115. var btn = $id('view_style_img'),
  116. rp = $id('reader_pane');
  117. if (((rp && rp.classList.contains('reader_pane_view_style_1')) || (btn && btn.hasAttribute('src') && btn.getAttribute('src') !== 'images/application-image.png')) && (e.keyCode === 32 || e.keyCode === exSpaceCode || e.keyCode === exSSpaceCode)) {
  118. var ce = currentEntry();
  119. if (key === 'down') {
  120. if (ce && ce.id) return ce.id;
  121. else if (articleID) return articleID;
  122. else return '';
  123. } else if (key === 'up') {
  124. if (ce && ce.id && ce.id !== articleID) {
  125. return true;
  126. } else return false;
  127. } else return false;
  128. } else return false;
  129. };
  130.  
  131. var exKey = function() {
  132. if ($id('irkc_key-PageUp')) {
  133. exUpKey = $id('irkc_key-PageUp').value;
  134. if (exUpKey) {
  135. exUpCode = (/^[%&]$/.test(exUpKey)) ? 32 : exUpKey.toUpperCase().charCodeAt(0);
  136. exUpShift = (isNaN(exUpKey) && exUpKey === exUpKey.toUpperCase() && exUpKey !== '&') ? true : false;
  137. }
  138. }
  139. if ($id('irkc_key-PageDown')) {
  140. exDownKey = $id('irkc_key-PageDown').value;
  141. if (exDownKey) {
  142. exDownCode = (/^[%&]$/.test(exDownKey)) ? 32 : exDownKey.toUpperCase().charCodeAt(0);
  143. exDownShift = (isNaN(exDownKey) && exDownKey === exDownKey.toUpperCase() && exDownKey !== '&') ? true : false;
  144. }
  145. }
  146. if ($id('irkc_key-space')) {
  147. exSpaceKey = $id('irkc_key-space').value;
  148. if (exSpaceKey) {
  149. exSpaceCode = (/^[%&]$/.test(exSpaceKey)) ? 32 : exSpaceKey.toUpperCase().charCodeAt(0);
  150. exSpaceShift = (isNaN(exSpaceKey) && exSpaceKey === exSpaceKey.toUpperCase() && exSpaceKey !== '&') ? true : false;
  151. }
  152. }
  153. if ($id('irkc_key-SPACE')) {
  154. exSSpaceKey = $id('irkc_key-SPACE').value;
  155. if (exSSpaceKey) {
  156. exSSpaceCode = (/^[%&]$/.test(exSSpaceKey)) ? 32 : exSSpaceKey.toUpperCase().charCodeAt(0);
  157. exSSpaceShift = (isNaN(exSSpaceKey) && exSSpaceKey === exSSpaceKey.toUpperCase() && exSSpaceKey !== '&') ? true : false;
  158. }
  159. }
  160. if ($id('irkc_key-o')) {
  161. exOpenKey = $id('irkc_key-o').value;
  162. if (exOpenKey) {
  163. exOpenCode = (/^[%&]$/.test(exOpenKey)) ? 32 : exOpenKey.toUpperCase().charCodeAt(0);
  164. exOpenShift = (isNaN(exOpenKey) && exOpenKey === exOpenKey.toUpperCase() && exOpenKey !== '&') ? true : false;
  165. }
  166. }
  167. };
  168.  
  169. var checkIrkc = function() {
  170. if ($id('irkc_ok')) {
  171. window.clearInterval(iIrkc);
  172. $id('irkc_ok').addEventListener('click', function() {
  173. exKey();
  174. }, false);
  175. exKey();
  176. } else if (nCheck < 99) nCheck++;
  177. else window.clearInterval(iIrkc);
  178. };
  179.  
  180. var viewSettings = function() {
  181. if ($id('irsm_settings').style.display === 'block') {
  182. $id('irsm_settings').style.display = 'none';
  183. return;
  184. }
  185. $id('irsm_s_time').value = st.time;
  186. $id('irsm_s_key_page').checked = (st.key_page) ? true : false;
  187. $id('irsm_s_key_space').checked = (st.key_space) ? true : false;
  188. $id('irsm_settings').style.display = 'block';
  189. };
  190.  
  191. var loadSettings = function() {
  192. st = {};
  193. try {
  194. st = JSON.parse(localStorage.getItem('InoReaderScrollMarker_settings')) || {};
  195. } catch (er) {
  196. window.alert('IRSM Error: Load Settings');
  197. }
  198. var notB = function(a) {
  199. return (typeof a !== 'boolean') ? true : false;
  200. };
  201. var notN = function(a) {
  202. return (typeof a !== 'number') ? true : false;
  203. };
  204. if (notN(st.time)) st.time = nTime;
  205. if (notB(st.key_page)) st.key_page = true;
  206. if (notB(st.key_space)) st.key_space = true;
  207. };
  208.  
  209. var saveSettings = function() {
  210. try {
  211. localStorage.setItem('InoReaderScrollMarker_settings', JSON.stringify(st));
  212. } catch (er) {
  213. window.alert('IRSM Error: Save Settings');
  214. }
  215. };
  216.  
  217. var div = document.createElement('div');
  218. div.id = 'irsm_settings';
  219. document.body.appendChild(div);
  220. $id('irsm_settings').innerHTML = '<div id="irsm_s_titlebar"><div id="irsm_s_title"><a href="https://greasyfork.org/scripts/894-inoreader-scroll-marker" target="_blank">InoReader Scroll Marker ' + loc[0] + '</a></div><div id="irsm_s_btn"><input type="button" id="irsm_s_ok" value="OK"><input type="button" id="irsm_s_cancel" value="' + loc[1] + '"></div></div><div id="irsm_s_body"><label>' + loc[2] + '<input id="irsm_s_time" type="text" maxlength="4"></label><fieldset><legend>' + loc[3] + ' : </legend><label><input id="irsm_s_key_page" type="checkbox">PageUp / PageDown</label><br><label><input id="irsm_s_key_space" type="checkbox">Space / Shift+Space</label></fieldset></div>';
  221.  
  222. var menu = $id('sb_rp_settings_menu'),
  223. pqm = $id('preferences_quick_main'),
  224. item = document.createElement('div');
  225. item.id = 'irsm_settings-menu';
  226. item.innerHTML = 'Scroll Marker ' + loc[0];
  227. if (menu) {
  228. item.className = 'inno_toolbar_button_menu_item';
  229. var menuList = menu.children;
  230. if (!menuList[menuList.length - 1].id) {
  231. var line = document.createElement('div');
  232. line.className = 'inno_toolbar_button_menu_line';
  233. menu.insertBefore(line, menu.lastChild.nextSibling);
  234. }
  235. menu.insertBefore(item, menu.lastChild.nextSibling);
  236. } else if ($id('quick_options') && pqm) {
  237. item.className = 'quick_options_link';
  238. pqm.insertBefore(item, pqm.lastChild.nextSibling);
  239. }
  240.  
  241. document.addEventListener('keydown', function(e) {
  242. var eRp = $id('reader_pane'),
  243. eTwc = $id('three_way_contents');
  244. if (eRp && !/^input|^textarea/i.test(e.target.tagName) && st.time !== 0) {
  245. if (
  246. (st.key_page && e.keyCode === 33) ||
  247. (st.key_page && e.keyCode === 34) ||
  248. (st.key_page && exUpKey && e.keyCode === exUpCode) ||
  249. (st.key_page && exDownKey && e.keyCode === exDownCode) ||
  250. (st.key_space && e.keyCode === 32 && exSpaceKey !== '&') ||
  251. (st.key_space && e.keyCode === 32 && e.shiftKey && exSSpaceKey !== '%') ||
  252. (st.key_space && exSpaceKey && e.keyCode === exSpaceCode) ||
  253. (st.key_space && exSSpaceKey && e.keyCode === exSSpaceCode)
  254. ) {
  255. if (
  256. (exUpCode !== exDownCode && (
  257. (e.keyCode === exUpCode && e.shiftKey !== exUpShift) ||
  258. (e.keyCode === exDownCode && e.shiftKey !== exDownShift)
  259. )) ||
  260. (exSpaceCode !== exSSpaceCode && (
  261. (e.keyCode === exSpaceCode && e.shiftKey !== exSpaceShift) ||
  262. (e.keyCode === exSSpaceCode && e.shiftKey !== exSSpaceShift)
  263. ))
  264. ) return;
  265. articleID = checkArticle(e, 'down');
  266. ismRp.style.display = 'none';
  267. ismTwc.style.display = 'none';
  268. nScrPos = (eTwc.offsetParent) ? eTwc.scrollTop : eRp.scrollTop;
  269. if (e.shiftKey) shift = true;
  270. } else if (e.keyCode === 13 || (!exOpenKey && e.keyCode === 79) || (exOpenKey && e.keyCode === exOpenCode && e.shiftKey === exOpenShift)) {
  271. var ac = currentEntry();
  272. if (ac) {
  273. window.clearTimeout(iMarker);
  274. ismRp.style.display = 'none';
  275. ismTwc.style.display = 'none';
  276. }
  277. } else if (ismRp.style.display !== 'none') {
  278. window.clearTimeout(iMarker);
  279. ismRp.style.display = 'none';
  280. } else if (ismTwc.style.display !== 'none') {
  281. window.clearTimeout(iMarker);
  282. ismTwc.style.display = 'none';
  283. }
  284. }
  285. }, true);
  286.  
  287. document.addEventListener('keyup', function(e) {
  288. var eRp = $id('reader_pane'),
  289. eTwc = $id('three_way_contents'),
  290. soh = 0,
  291. sst = 0;
  292. if (eRp && !/^input|^textarea/i.test(e.target.tagName) && st.time !== 0) {
  293. var bTwc = (eTwc.offsetParent) ? true : false;
  294. soh = (bTwc) ? eTwc.offsetHeight : eRp.offsetHeight;
  295. sst = (bTwc) ? eTwc.scrollTop : eRp.scrollTop;
  296. if (
  297. (st.key_page && e.keyCode === 33) ||
  298. (st.key_page && exUpKey && e.keyCode === exUpCode && (e.shiftKey === exUpShift || shift)) ||
  299. (st.key_space && e.keyCode === 32 && e.shiftKey && exSSpaceKey !== '%') ||
  300. (st.key_space && exSSpaceKey && e.keyCode === exSSpaceCode && (e.shiftKey === exSSpaceShift || shift))
  301. ) {
  302. if (nScrPos !== sst) {
  303. if (checkArticle(e, 'up')) {
  304. articleID = '';
  305. return;
  306. }
  307. if (bTwc) {
  308. ismTwc.style.display = 'block';
  309. ismTwc.style.top = (nScrPos - (ismTwc.scrollHeight / 2)) + 'px';
  310. } else {
  311. ismRp.style.display = 'block';
  312. ismRp.style.top = (nScrPos - (ismRp.scrollHeight / 2)) + 'px';
  313. }
  314. window.clearTimeout(iMarker);
  315. if (st.time > 0) {
  316. iMarker = window.setTimeout(function() {
  317. window.clearTimeout(iMarker);
  318. ismRp.style.display = 'none';
  319. ismTwc.style.display = 'none';
  320. }, st.time);
  321. }
  322. }
  323. shift = false;
  324. } else if (
  325. (st.key_page && e.keyCode === 34) ||
  326. (st.key_page && exDownKey && e.keyCode === exDownCode && (e.shiftKey === exDownShift || shift)) ||
  327. (st.key_space && e.keyCode === 32 && !e.shiftKey && exSpaceKey !== '&') ||
  328. (st.key_space && exSpaceKey && e.keyCode === exSpaceCode && (e.shiftKey === exSpaceShift || shift))
  329. ) {
  330. if (nScrPos !== sst) {
  331. if (checkArticle(e, 'up')) {
  332. articleID = '';
  333. return;
  334. }
  335. if (bTwc) {
  336. ismTwc.style.display = 'block';
  337. ismTwc.style.top = (nScrPos + soh - (ismTwc.scrollHeight / 2)) + 'px';
  338. } else {
  339. ismRp.style.display = 'block';
  340. ismRp.style.top = (nScrPos + soh - (ismRp.scrollHeight / 2)) + 'px';
  341. }
  342. window.clearTimeout(iMarker);
  343. if (st.time > 0) {
  344. iMarker = window.setTimeout(function() {
  345. window.clearTimeout(iMarker);
  346. ismRp.style.display = 'none';
  347. ismTwc.style.display = 'none';
  348. }, st.time);
  349. }
  350. }
  351. shift = false;
  352. }
  353. }
  354. }, true);
  355.  
  356. document.addEventListener('click', function(e) {
  357. if (e.button >= 2) return;
  358. if (e.target.id === 'irsm_settings-menu') {
  359. viewSettings();
  360. } else if (e.target.id === 'irsm_s_ok') {
  361. var time = $id('irsm_s_time').value,
  362. problem = false;
  363. if (time === '' || /^\s+$/.test(time)) st.time = nTime;
  364. else if (time && !isNaN(time)) st.time = Number(time);
  365. else problem = true;
  366. if (!problem) {
  367. st.key_page = $id('irsm_s_key_page').checked;
  368. st.key_space = $id('irsm_s_key_space').checked;
  369. $id('irsm_s_ok').blur();
  370. $id('irsm_settings').style.display = 'none';
  371. saveSettings();
  372. }
  373. } else if (e.target.id === 'irsm_s_cancel') {
  374. $id('irsm_s_cancel').blur();
  375. $id('irsm_settings').style.display = 'none';
  376. } else if (ismRp.style.display !== 'none') {
  377. window.clearTimeout(iMarker);
  378. ismRp.style.display = 'none';
  379. } else if (ismTwc.style.display !== 'none') {
  380. window.clearTimeout(iMarker);
  381. ismTwc.style.display = 'none';
  382. }
  383. }, false);
  384.  
  385. loadSettings();
  386. iIrkc = window.setInterval(function() {
  387. checkIrkc();
  388. }, 250);
  389.  
  390. $id('irsm_s_titlebar').addEventListener('dblclick', function(e) {
  391. if (e.target.nodeName === 'DIV') {
  392. $id('irsm_s_body').classList.toggle('irsm_s_hide');
  393. $id('irsm_s_btn').classList.toggle('irsm_s_hide');
  394. }
  395. }, false);
  396. };
  397.  
  398. iInit = window.setInterval(function() {
  399. if ($id('tree') && $id('tree').innerHTML) {
  400. window.clearInterval(iInit);
  401. window.setTimeout(function() {
  402. init();
  403. }, 1000);
  404. }
  405. }, 500);
  406.  
  407. })();