InoReader Scroll Marker

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

当前为 2014-06-13 提交的版本,查看 最新版本

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