fork download
  1. // ==UserScript==
  2. // @name shitaraba_option
  3. // @namespace john1851
  4. // @description shitaraba_option
  5. // @include http://c...content-available-to-author-only...a.net/internet/21187/config/data/thread_delete*
  6. // @exclude http://c...content-available-to-author-only...a.net/internet/21187/config/data/thread_delete_confirm
  7. // @exclude http://c...content-available-to-author-only...a.net/internet/21187/config/data/thread_delete_finish
  8. // @grant none
  9. // @version 0.5.0.00
  10. // ==/UserScript==
  11.  
  12.  
  13. (function (_, count) {
  14. _('content', function (board, threads) {
  15. console.log('今日は' + count + '回目の起動の日です。おめでとうございます。');
  16.  
  17. board.click(function (type, isForward) {
  18. board.draw(sort(threads, type, isForward));
  19. });
  20. board.draw(threads);
  21. });
  22.  
  23. function align(array, name, isForward, action) {
  24. if (isForward)
  25. return array.sort(function (x, y) { return action(x[name], y[name]); });
  26. return array.sort(function (x, y) { return action(y[name], x[name]); });
  27. }
  28. function sort(threads, type, isForward) {
  29. if (['no', 'title', 'response', 'newResponse', 'vigor'].indexOf(type) < 0)
  30. throw new RangeError('not support type: ' + type);
  31.  
  32. if (type === 'title') {
  33. return align(threads, type, isForward, function (x, y) {
  34. return x < y ? -1 : x > y ? 1 : 0;
  35. });
  36. }
  37. return align(threads, type, isForward, function (x, y) {
  38. return x - y;
  39. });
  40. }
  41. })((function () {
  42. function array(str) {
  43. return new Function('return [' + str + '];')();
  44. }
  45. function project(array, name) {
  46. return array.map(function (x) { return x[name]; });
  47. }
  48. function zip() {
  49. var results = [],
  50. arrays = [].slice.call(arguments, 0),
  51. length = Math.max.apply(null, project(arrays, 'length'));
  52. for (var i = 0; i < length; i++) {
  53. results.push(project(arrays, i));
  54. }
  55. return results;
  56. }
  57. function format(date) {
  58. var m = (date.getMonth() + 1) + '/',
  59. d = date.getDate() + '/',
  60. h = date.getHours() + ':',
  61. min = date.getMinutes();
  62. return m + d + h + min;
  63. }
  64. function interval(date) {
  65. var sec = Math.floor(new Date() / 1000 - date / 1000),
  66. min, hour, day, mon, times, unit, result;
  67. min = Math.floor(sec / 60);
  68. sec = sec % 60;
  69. hour = Math.floor(min / 60);
  70. min = min % 60;
  71. day = Math.floor(hour / 24);
  72. hour = hour % 24;
  73. mon = Math.floor(day / 30);
  74. day = day % 30;
  75.  
  76. times = [mon, day, hour, min, sec];
  77. unit = ['ヶ月', '日', '時間', '分', '秒'];
  78. result = '';
  79. for (var i = 0; i < times.length; i++) if (times[i] !== 0) {
  80. result += times[i] + unit[i];
  81. if (i != times.length - 1)
  82. result += times[i + 1] + unit[i + 1];
  83. break;
  84. }
  85. return result + '前';
  86. }
  87.  
  88. function $(tag, option) {
  89. var children = [].slice.call(arguments, 2),
  90. element = document.createElement(tag);
  91. $.option(element, option || {});
  92. children.forEach(function (x) { element.appendChild(x); });
  93. return element;
  94. }
  95. $.attribute = function (element, attributes) {
  96. Object.keys(attributes).filter(function (x) {
  97. return ['klass', 'text'].indexOf(x) < 0;
  98. }).forEach(function (x) {
  99. element.setAttribute(x, attributes[x]);
  100. });
  101. return element;
  102. };
  103. $.option = function (element, option) {
  104. if (option.klass)
  105. element.classList.add(option.klass);
  106. if (option.text)
  107. element.textContent = option.text;
  108. return $.attribute(element, option);
  109. };
  110. $.find = function (id, child) {
  111. if (child)
  112. return document.getElementById(id).getElementsByTagName(child)[0];
  113. return document.getElementById(id);
  114. };
  115. $.clone = function (node, option) {
  116. var element = node.cloneNode(true);
  117. return $.option(element, option || {});
  118. };
  119. $.prepend = function (node, child) {
  120. node.insertBefore(child, node.firstElementChild);
  121. return node;
  122. };
  123.  
  124. function threads(id) {
  125. var table = $.find(id).getElementsByTagName('table')[0],
  126. tr = table.getElementsByTagName('tr'),
  127. th = tr[0].children;
  128.  
  129. return [].slice.call(tr, 1).map(function (x) {
  130. return x.children;
  131. }).map(function (x, i) {
  132. var date = new Date(),
  133. response = parseInt(x[3].textContent, 10),
  134. url = x[2].children[1].textContent.split('/').pop();
  135. date.setTime(parseInt(url, 10) * 1000);
  136. return {
  137. check: x[0].children[0].checked,
  138. no: parseInt(x[1].textContent, 10),
  139. title: x[2].children[0].textContent,
  140. url: url,
  141. date: date,
  142. response: response,
  143. vigor: (function () {
  144. var timestamp = new Date() / 1000 - date / 1000,
  145. vigor = response / (timestamp / (60 * 60 * 24));
  146. if (response <= 1)
  147. return 0;
  148. else if (response <= 10)
  149. return vigor * response / 10;
  150. return vigor;
  151. })()
  152. };
  153. });
  154. }
  155.  
  156. function view() {
  157. return $('tbody', {},
  158. $('tr', {},
  159. $('th', { klass: 'checkbox', text: '削除' }),
  160. $('th', { klass: 'no', 'data-type': 'no', text: 'No.', style: 'cursor: pointer;' }),
  161. $('th', { klass: 'title', 'data-type': 'title', text: 'タイトル', style: 'cursor: pointer;' }),
  162. $('th', { klass: 'resNumber', 'data-type': 'response', text: 'レス数', style: 'cursor: pointer;' }),
  163. $('th', { klass: 'resNumber', 'data-type': 'newResponse', text: '新レス数', style: 'cursor: pointer;' }),
  164. $('th', { klass: 'resNumber', 'data-type': 'vigor', text: '勢い', style: 'cursor: pointer;' }),
  165. $('th', { klass: 'individualDeletion' }),
  166. $('th', { klass: 'infectionDeletion' })
  167. )
  168. );
  169. }
  170. view.row = function (thread, i) {
  171. function checkbox(url) {
  172. return $('td', { klass: 'checkbox' },
  173. $('input', {
  174. type: 'checkbox', name: 'key_' + url
  175. })
  176. );
  177. }
  178. function no(no) {
  179. return $('td', { klass: 'no', text: no });
  180. }
  181. function title(title, url, date, isNew) {
  182. var element = $('td', { klass: 'title' },
  183. $('a', {
  184. target: '_blank', text: title,
  185. href: 'http://j...content-available-to-author-only...a.net/bbs/read.cgi/internet/' + url + '/1405372778/l50'
  186. }),
  187. $('p', {
  188. klass: 'searchURL', text: format(date) + ' '
  189. }, $('b', { text: interval(date) }))
  190. );
  191. if (isNew)
  192. $.prepend(element, $('span', { text: '(New) ', style: 'color: #eb6101;' }));
  193. return element;
  194. }
  195. function response(response) {
  196. return $('td', { klass: 'resNumber', text: response });
  197. }
  198. function newResponse(newResponse, isNew) {
  199. var element = $('td', { klass: 'resNumber' },
  200. $('i', { text: '(' + newResponse + ')' })
  201. );
  202. if (isNew)
  203. $.option(element.firstElementChild, { style: 'color: red;' });
  204. else
  205. $.option(element.firstElementChild, { style: 'color: blue;' });
  206. return element;
  207. }
  208. function vigor(vigor) {
  209. return $('td', { klass: 'resNumber', style: 'overflow: hidden;' },
  210. $('b', {
  211. text: (function () {
  212. if (vigor < 1)
  213. return vigor.toFixed(2);
  214. else if (vigor < 100)
  215. return vigor.toFixed(1);
  216. else if (vigor < 1000)
  217. return vigor.toFixed(0);
  218. return '999+';
  219. })()
  220. })
  221. );
  222. }
  223. function save(url) {
  224. return $('td', { klass: 'individualDeletion' },
  225. $('a', {
  226. text: '倉庫へ送る',
  227. href: './thread_delete_confirm?key=' + url + '&subcommand=save'
  228. })
  229. );
  230. }
  231. function remove(url) {
  232. return $('td', { klass: 'infectionDeletion' },
  233. $('a', {
  234. text: '完全削除',
  235. href: './thread_delete_confirm?key=' + url + '&subcommand=remove'
  236. })
  237. );
  238. }
  239.  
  240. return $('tr', { klass: i % 2 === 0 ? 'even' : 'odd' },
  241. checkbox(thread.url),
  242. no(thread.no),
  243. title(thread.title, thread.url, thread.date, thread.isNew),
  244. response(thread.response),
  245. newResponse(thread.newResponse, thread.isNew),
  246. vigor(thread.vigor),
  247. save(thread.url),
  248. remove(thread.url)
  249. );
  250. };
  251.  
  252. function board(id) {
  253. var table = $.find(id, 'table');
  254. table.replaceChild(view(), table.firstElementChild);
  255. return {
  256. draw: function (threads) {
  257. [].slice.call(table.firstElementChild.children, 1).forEach(function (x) {
  258. table.firstElementChild.removeChild(x);
  259. });
  260. threads.forEach(function (x, i) {
  261. table.firstElementChild.appendChild(view.row(x, i));
  262. });
  263. },
  264. click: function (callback) {
  265. [].slice.call(table.firstElementChild.firstElementChild.children, 1, -2).forEach(function (x) {
  266. x.addEventListener('click', function () {
  267. var type = x.dataset['type'],
  268. value = sessionStorage[type],
  269. isForward = typeof value === 'undefined' ? true : value === 'true';
  270. callback(type, isForward);
  271. sessionStorage[type] = !isForward;
  272. }, false);
  273. });
  274. }
  275. };
  276. }
  277.  
  278. function autoCheck(id) {
  279. var form = $.find(id, 'table').parentNode,
  280. button = $('input', { type: 'button', value: '自動チェック' }),
  281. tip = $('b', { text: '現在の総スレ数: ' + threads(id).length });
  282.  
  283. button.addEventListener('click', function () {
  284. var elements = document.forms[0].elements,
  285. value = sessionStorage['autoCheck'],
  286. toggle = typeof value === 'undefined' ? true : value === 'true';
  287. if (toggle)
  288. {
  289. zip(threads(id), elements).filter(function (x) {
  290. return typeof x[0] !== 'undefined';
  291. }).sort(function (x, y) {
  292. return x[0].vigor - y[0].vigor;
  293. }).slice(0, 30).forEach(function (x) {
  294. x[1].checked = true;
  295. });
  296. }
  297. else
  298. {
  299. for (var i = 0, length = elements.length; i < length; i++) {
  300. elements[i].checked = false;
  301. }
  302. }
  303. sessionStorage['autoCheck'] = !toggle;
  304. }, false);
  305. form.parentNode.insertBefore(button, form);
  306. form.parentNode.insertBefore(tip, form);
  307. }
  308.  
  309. return function (id, callback) {
  310. var _threads = threads(id),
  311. responses = array(localStorage['responses'] || project(_threads, 'response'));
  312. responses = new Array(_threads.length - responses.length).concat(responses);
  313.  
  314. zip(_threads, responses).forEach(function (x) {
  315. x[0].newResponse = x[0].response - (x[1] || 0);
  316. x[0].isNew = typeof x[1] === 'undefined';
  317. });
  318.  
  319. autoCheck(id);
  320. callback(board(id), _threads);
  321.  
  322. localStorage['responses'] = project(_threads, 'response').toString();
  323. localStorage['count'] = (parseInt(localStorage['count'], 10) || 1) + 1;
  324. };
  325. })(), parseInt(localStorage['count'], 10) || 1);
  326.  
Runtime error #stdin #stdout #stderr 0.02s 5068KB
stdin
Standard input is empty
stdout
Standard output is empty
stderr
prog.js:325: ReferenceError: localStorage is not defined