您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
CafeCoder のUIを改善し,コンテストを快適にします(たぶん)
- // ==UserScript==
- // @name CafeCoder Enhancer
- // @namespace iilj
- // @version 2020.01.05.5
- // @description CafeCoder のUIを改善し,コンテストを快適にします(たぶん)
- // @author iilj
- // @supportURL https://github.com/iilj/CafeCodeEnhancer/issues
- // @match https://www.cafecoder.top/*
- // @require https://cdnjs.cloudflare.com/ajax/libs/noty/3.1.4/noty.js
- // @require https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.48.4/codemirror.js
- // @require https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.48.4/mode/clike/clike.js
- // @require https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.48.4/mode/python/python.js
- // @require https://cdnjs.cloudflare.com/ajax/libs/list.js/1.5.0/list.js
- // @resource css_noty https://cdnjs.cloudflare.com/ajax/libs/noty/3.1.4/noty.css
- // @resource css_cm https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.48.4/codemirror.css
- // @grant GM_addStyle
- // @grant GM_getResourceText
- // ==/UserScript==
- /* globals CodeMirror, Noty, List */
- (function () {
- 'use strict';
- GM_addStyle(GM_getResourceText('css_noty'));
- GM_addStyle(GM_getResourceText('css_cm'));
- GM_addStyle(`
- /* h4 まわりの UI 改善 */
- h4 {
- margin-top: 1rem;
- border-bottom: 2px solid lightblue;
- border-left: 10px solid lightblue;
- padding-left: 0.5rem;
- }
- /* ページ最下部のコンテンツが見やすいように調整する */
- div.card {
- marginBottom: 30px;
- }
- /* コンテストページ上部のメニューを使いやすくする */
- div.card-body a.nav-item.nav-link {
- border: 1px solid #bbbbbb;
- margin: 0.3rem;
- border-radius: 0.3rem;
- color: #007bff;
- }
- div.card-body a.nav-item.nav-link.cce-active {
- background-color: #ffffff;
- color: rgba(0,0,0,.5);
- }
- div.card-body a.nav-item.nav-link:hover{
- background-color: #dddddd;
- }
- /* 入出力サンプルのUI */
- .cce-myprenode {
- display: block;
- margin: 0.4rem;
- padding: 0.4rem;
- background-color: #efefef !important;
- border: 1px solid #bbbbbb;
- border-radius: 0.4rem;
- font-family: Menlo,Monaco,Consolas,"Courier New",monospace;
- }
- .CodeMirror {
- border-top: 1px solid black;
- border-bottom: 1px solid black;
- }
- /* sortable table */
- table.table th {
- padding: 6px;
- vertical-align: middle;
- }
- table.table tbody th {
- font-weight: normal; /* hotfix for unformal use of th tag */
- position: relative;
- }
- table thead th[data-sort] {
- cursor: pointer;
- color: #007bff;
- }
- table thead th[data-sort]:hover {
- background-color: #dddddd;
- }
- table thead th[data-sort].sort.desc:after {
- content: " ▲";
- color: #888;
- }
- table thead th[data-sort].sort.asc:after {
- content: " ▼";
- color: #888;
- }
- /* result icon */
- span.result, th.result>span {
- display: inline;
- padding: .2em .6em .3em;
- font-size: 75%;
- font-weight: bold;
- line-height: 1;
- color: #fff;
- text-align: center;
- white-space: nowrap;
- vertical-align: baseline;
- border-radius: .25em;
- border: none;
- -webkit-text-stroke: unset;
- text-shadow: none;
- margin: 0;
- cursor: default;
- }
- .AC {
- background-color: #5cb85c;
- }
- .WA, .TLE {
- background-color: #f0ad4e;
- }
- .WJ {
- background-color: #777;
- }
- /* ranking page */
- table.table.cce-ranking-table th {
- padding: 10px;
- }
- div.point {
- height: auto;
- width: auto;
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translateY(-50%) translateX(-50%);
- -webkit-transform: translateY(-50%) translateX(-50%);
- }
- div.point a {
- color: #00AA3E;
- font-weight: bold;
- }
- div.point span.submit_time {
- margin: 0 0 3px;
- color: #888;
- font-size: 90%;
- font-weight: normal;
- }
- table.table th.cce-ranking-username {
- font-weight: bold;
- width: auto;
- height: auto;
- }
- .cce-ranking-point div.point {
- color: blue;
- font-weight: bold;
- }
- /* icon */
- @font-face {
- font-family: 'Glyphicons Halflings';
- src: url('https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/fonts/glyphicons-halflings-regular.eot');
- src: url('https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'),
- url('https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/fonts/glyphicons-halflings-regular.woff2') format('woff2'),
- url('https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/fonts/glyphicons-halflings-regular.woff') format('woff'),
- url('https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/fonts/glyphicons-halflings-regular.ttf') format('truetype'),
- url('https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg')
- }
- .glyphicon {
- position: relative;
- top: 1px;
- display: inline-block;
- font-family: 'Glyphicons Halflings';
- font-style: normal;
- font-weight: normal;
- line-height: 1;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
- margin-right: 0.2rem;
- }
- .glyphicon-home:before {
- content: "\\e021"
- }
- .glyphicon-tasks:before {
- content: "\\e137"
- }
- .glyphicon-sort-by-attributes-alt:before {
- content: "\\e156"
- }
- .glyphicon-user:before {
- content: "\\e008"
- }
- .glyphicon-list:before {
- content: "\\e056"
- }
- * {
- -webkit-box-sizing: border-box;
- -moz-box-sizing: border-box;
- box-sizing: border-box
- }
- *:before,
- *:after {
- -webkit-box-sizing: border-box;
- -moz-box-sizing: border-box;
- box-sizing: border-box
- }
- `);
- const msg = (type, text) => {
- new Noty({
- type: type,
- layout: 'top',
- timeout: 3000,
- text: text
- }).show();
- };
- const dqs = (selectors) => document.querySelector(selectors);
- const dqsa = (selectors) => document.querySelectorAll(selectors);
- // add title tag when there exists no title tag
- let result;
- if (!dqs("title")) {
- const title = document.createElement("title");
- let stitle = "";
- if (result = location.href.match(/www\.cafecoder\.top\/([^\/]+)\/(index\.(php|html?))?$/)) {
- stitle += `${result[1]} (${dqs("h1").innerText.trim()})`;
- } else if (result = location.href.match(/www\.cafecoder\.top\/([^\/]+)\/problem_list\.(php|html?)$/)) {
- stitle += `${result[1]} 問題一覧`;
- } else if (result = location.href.match(/www\.cafecoder\.top\/([^\/]+)\/Problems\/([^\/]+)\.(php|html?)$/)) {
- stitle += `${result[1]}-${result[2]}`;
- const h3 = dqs("h3");
- if (h3) {
- stitle += " " + h3.innerText.trim();
- }
- }
- stitle += (stitle == "" ? "" : " : ") + "CafeCoder";
- title.innerText = stitle;
- dqs("head").insertAdjacentElement('afterbegin', title);
- }
- // fix invalid/broken uri
- dqsa("a[href*='kakecoder.com']").forEach((lnk) => {
- lnk.href = lnk.href.replace('kakecoder.com', 'cafecoder.top');
- });
- dqsa("a[href*='.html']").forEach((lnk) => {
- lnk.href = lnk.href.replace('.html', '.php');
- });
- dqsa("a[href^='//'][href$='.php']").forEach((lnk) => {
- const href = lnk.getAttribute('href');
- if (result = href.match(/^\/\/([^\/]+)\.(php|html?)$/)) {
- lnk.setAttribute('href', href.replace('//', location.href.indexOf("/Problems/") != -1 ? '../' : './'));
- }
- });
- // add icon
- dqsa('div.card-body a.nav-item.nav-link').forEach((lnk) => {
- const href = lnk.getAttribute('href');
- let type = 'home';
- if (result = href.match(/([^\/]+).(php|html?)(\?[^/]+)?$/)) {
- const nm = result[1];
- switch (nm) {
- case 'index':
- type = 'home';
- break;
- case 'problem_list':
- type = 'tasks';
- break;
- case 'ranking':
- type = 'sort-by-attributes-alt';
- break;
- case 'my_submit':
- type = 'user';
- break;
- case 'all_submit':
- type = 'list';
- break;
- }
- }
- const icon = document.createElement("span");
- icon.classList.add('glyphicon', `glyphicon-${type}`);
- icon.setAttribute('aria-hidden', 'true');
- lnk.insertAdjacentElement('afterbegin', icon);
- if (lnk.href == location.href) {
- lnk.classList.add('cce-active');
- }
- });
- // when problem page
- if (location.href.indexOf("/Problems/") != -1 && dqs("h3") != null) {
- // improve UI/UX of I/O sample, and add sample copy button feature
- dqsa("span[style]:not([class]), pre[style]:not([class]), div[style]:not([class]), .sample").forEach((node, idx, _nodelist) => {
- if (!node.classList.contains('sample') && !node.style.backgroundColor && node.getAttribute("style").indexOf("background-color") == -1) {
- return;
- }
- node.classList.add('cce-myprenode');
- node.id = `cce-myprenode-${idx}`;
- if (node.firstChild.nodeName == "#text") {
- node.firstChild.data = node.firstChild.data.trim();
- }
- if (node.lastChild.nodeName == "#text") {
- node.lastChild.data = node.lastChild.data.trim();
- }
- let btn = document.createElement("button");
- btn.innerText = "テキストをコピー!";
- btn.classList.add('btn', 'btn-primary', 'copy-sample-input');
- btn.style.display = "block";
- btn.addEventListener("click", () => {
- const elem = document.getElementById(node.id);
- document.getSelection().selectAllChildren(elem);
- if (document.execCommand("copy")) {
- msg('success', 'テキストをコピーしました!');
- document.getSelection().removeAllRanges();
- } else {
- msg('error', 'コピーに失敗してしまったようです.');
- }
- }, false);
- node.insertAdjacentElement('beforebegin', btn);
- });
- // CodeMirror init
- const textarea = dqs('form[name=submit_form] textarea[name=sourcecode]');
- const editor = CodeMirror.fromTextArea(textarea, {
- mode: "text/x-c++src",
- lineNumbers: true,
- });
- // CodeMirror lang selection changed event handler
- const selectlang = dqs('form[name=submit_form] select[name=language]');
- selectlang.addEventListener('change', (event) => {
- const modelist = [
- 'text/x-csrc', 'text/x-c++src', 'text/x-java', 'python', 'text/x-csharp'
- ];
- editor.setOption("mode", modelist[event.target.selectedIndex]);
- });
- // select lang C++17 as a default
- selectlang.selectedIndex = 1;
- selectlang.classList.add('form-control');
- // CodeMirror submit preprocess, remove default broken event
- const submitbtn = dqs('form[name=submit_form] input[type=submit]');
- submitbtn.removeAttribute("onclick");
- submitbtn.classList.add('btn-primary');
- document.submit_form.addEventListener('submit', (event) => {
- editor.save();
- if (textarea.value == '') {
- msg('warning', 'ソースコードが入力されていません');
- event.preventDefault();
- }
- });
- } else if (location.href.indexOf("/all_submit.php?") != -1 || location.href.indexOf("/my_submit.php?") != -1) {
- // on submit list page
- const parent = dqs('div.card-body');
- parent.id = 'cce-list-parent';
- const table = parent.querySelector('table.table');
- table.classList.add('table-striped', 'small');
- const tbody = table.querySelector('tbody');
- tbody.classList.add('list');
- tbody.querySelectorAll('tr').forEach((tr) => {
- tr.querySelectorAll('th').forEach((td, idx, nodelist) => { /* unformal html (th here should be td) */
- if (idx == nodelist.length - 1) {
- return;
- }
- td.classList.add(`cce-list-sort-${idx}`);
- });
- });
- parent.querySelector('thead').querySelectorAll('tr th').forEach((th, idx, nodelist) => {
- if (idx == nodelist.length - 1) {
- return;
- } else if (idx == 1) {
- th.classList.add('desc');
- }
- th.classList.add('sort');
- th.setAttribute('data-sort', `cce-list-sort-${idx}`);
- });
- const userList = new List(parent.id, {
- valueNames: ['cce-list-sort-0', 'cce-list-sort-1', 'cce-list-sort-2', 'cce-list-sort-3']
- });
- } else if (location.href.indexOf("/problem_list.") != -1) {
- // on contest problem list page
- const table = dqs('table.table');
- const thead = table.querySelector('thead tr');
- const th0 = document.createElement("th");
- th0.innerText = '#';
- thead.insertAdjacentElement('afterbegin', th0);
- table.querySelectorAll('tbody tr').forEach((tr) => {
- const tr0 = document.createElement("th");
- console.log(tr.querySelector('a[href]').href);
- const a0 = tr.querySelector('a[href]');
- if (result = a0.href.match(/\/([^\/])\.(php|html?)?$/)) {
- const pid = result[1];
- const a1 = document.createElement("a");
- a1.href = a0.href;
- a1.innerText = pid;
- tr0.insertAdjacentElement('afterbegin', a1);
- } else {
- tr0.innerText = '?';
- }
- tr.insertAdjacentElement('afterbegin', tr0);
- });
- } else if (location.href.indexOf("/ranking.php?") != -1) {
- // on contest ranking page
- const parent = dqs('div.card-body');
- parent.id = 'cce-list-parent';
- const table = parent.querySelector('table.table');
- table.classList.add('table-striped', 'small', 'cce-ranking-table');
- const tbody = table.querySelector('tbody');
- tbody.classList.add('list');
- tbody.querySelectorAll('tr').forEach((tr, tridx) => {
- let endtime = '00:00:00';
- const submit_time = document.createElement("span");
- submit_time.classList.add('submit_time');
- tr.querySelectorAll('th').forEach((td, idx, nodelist) => { /* unformal html (th here should be td) */
- if (idx == 1) {
- td.classList.add('cce-ranking-username');
- } else if (idx == 2) {
- td.classList.add('cce-ranking-point');
- td.setAttribute('data-cce-list-sort-point', `${tridx}`);
- const divpoint = td.firstElementChild;
- divpoint.insertAdjacentHTML('beforeend', '<br>');
- divpoint.insertAdjacentElement('beforeend', submit_time);
- } else if (idx >= 3) {
- const timespan = td.querySelector('span.submit_time');
- if (timespan) {
- td.setAttribute('data-cce-list-sort-timespan', timespan.innerText);
- if (timespan.innerText > endtime) {
- endtime = timespan.innerText;
- }
- } else {
- td.setAttribute('data-cce-list-sort-timespan', '99:99:99');
- }
- }
- td.classList.add(`cce-list-sort-${idx}`);
- });
- submit_time.innerText = endtime;
- });
- parent.querySelector('thead').querySelectorAll('tr th').forEach((th, idx, nodelist) => {
- if (idx == 0) {
- th.classList.add('asc');
- }
- th.classList.add('sort');
- th.setAttribute('data-sort', `cce-list-sort-${idx}`);
- });
- const userList = new List(parent.id, {
- valueNames: ['cce-list-sort-0', 'cce-list-sort-1',
- { name: 'cce-list-sort-2', attr: 'data-cce-list-sort-point' },
- { name: 'cce-list-sort-3', attr: 'data-cce-list-sort-timespan' },
- { name: 'cce-list-sort-4', attr: 'data-cce-list-sort-timespan' },
- { name: 'cce-list-sort-5', attr: 'data-cce-list-sort-timespan' },
- { name: 'cce-list-sort-6', attr: 'data-cce-list-sort-timespan' },
- { name: 'cce-list-sort-7', attr: 'data-cce-list-sort-timespan' },
- { name: 'cce-list-sort-8', attr: 'data-cce-list-sort-timespan' }]
- });
- }
- })();