Beautiful OpenJudge

使用 BootStrap 库美化 OpenJudge

当前为 2020-06-20 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Beautiful OpenJudge
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.1.1
  5. // @description 使用 BootStrap 库美化 OpenJudge
  6. // @author Guyutongxue
  7. // @match http://*.openjudge.cn/*
  8. // @grant none
  9. // @include https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. 'use strict';
  14.  
  15. // Load BootStrap 4
  16. document.head.innerHTML += '<meta name="viewport" content="width=device-width, initial-scale=1">\
  17. <link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.5.0/css/bootstrap.min.css">\
  18. <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.5.0/js/bootstrap.min.js"></script>\
  19. <style>\
  20. /* Fix styles*/\
  21. /* Titles*/\
  22. h4{font-size:inherit;}\
  23. h2,h3{font-size:18px;}\
  24. h1,h2,h3{font-weight:bold;}\
  25. h1{font-size:2em}\
  26. /*Headers*/\
  27. #headerTop{background:initial;}\
  28. #headerTop a{color:initial;}\
  29. #userToolbar, #headerTop .logo {font-size: inherit;}\
  30. #headerTop #userToolbar .current a.link{background:initial;}\
  31. .practice-search button{background:#545b62;font-size:12px;width:25px;padding:0px;}\
  32. #groupBigLogo,#groupBigLogo img{max-width: 100%;height: auto;}\
  33. #pageTitle{padding-bottom:0px;border-bottom:0px;margin-bottom:0px;}\
  34. .wrapper{width:initial;margin:0 10%;}\
  35. .appli-group{height:auto;}\
  36. /*Contest title*/\
  37. .recently-update, .over-time{position:relative;}\
  38. .label h3, .current-contest h3{border-bottom:0;}\
  39. .group-setting{font-size:smaller;}\
  40. /*Tables*/\
  41. table{line-height:initial;font-size:smaller;}\
  42. .my-solutions td.result{line-height:2.5em;}\
  43. #problemsList table tr{font-size:inherit;line-height:inherit}\
  44. .my-solutions{font-size:smaller;}\
  45. .my-solutions .time{width:auto;}\
  46. .practice-info table tr td{padding:.5em;}\
  47. table th{font-weight:bold;}\
  48. table thead tr{background:initial;}\
  49. /*Personal Page*/\
  50. .recently-submit table td{padding:.3rem;}\
  51. .recently-submit .contest{width:auto;}\
  52. .all-group li{float:left; overflow: initial;}\
  53. .my-group-logo{margin-right:15px;}\
  54. </style>\
  55. ';
  56. // Dealing with main header
  57. $("#headerTop").addClass('bg-light');
  58. $("#userToolbar").css('margin-bottom','0');
  59. $("#userToolbar").addClass('btn-group btn-group-sm');
  60. $("#userToolbar li").addClass('btn btn-sm btn-light');
  61. $(".account-list li").attr('class', 'btn btn-sm btn-link');
  62. $(".search-form").addClass('inline-form');
  63. $(".search-form input").addClass('form-control').attr('value','').attr('placeholder','题目ID, 标题, 描述');
  64. $("button").addClass('btn btn-secondary');
  65. $(".search-form button").html("&#10140;"); // right arrow
  66.  
  67. // Dealing with main container
  68. $("#pagebody,#sitePagebody,#footer .wrapper").attr('class','container');
  69. $("#pagebody .wrapper,#sitePagebody .wrapper").attr('class','row mt-3');
  70. $(".col-2").removeClass('col-2').addClass('col-md-2');
  71. $(".col-3").removeClass('col-3').addClass('col-md-3');
  72. $(".col-4").removeClass('col-4').addClass('col-md-4');
  73. $(".col-8").removeClass('col-8').addClass('col-md-8');
  74. $(".col-9").removeClass('col-9').addClass('col-md-9');
  75. $(".col-10").removeClass('col-10').addClass('col-md-10');
  76. $(".problem-page").removeClass('problem-page');
  77. $(".problem-statistics").removeClass('problem-statistics');
  78. $(".problem-status").removeClass('problem-status');
  79. $(".problem-my-statistics").removeClass('problem-my-statistics');
  80.  
  81. // Dealing with problem header
  82. $("#header").addClass("mb-4");
  83.  
  84. // Group index page
  85. $(".contest-info").removeClass('contest-info').addClass('d-flex justify-content-lg-between flex-lg-row flex-column').css('border-bottom','1px dotted #666666');
  86. $(".recently-update").remove();
  87. $(".practice-info h3,.coming-contest h3,.past-contest h3").css('border-bottom','1px dotted #666666');
  88. console.log($(".group-setting").children().text().replace(/\s+/g, ""));
  89. // If I'm in this group, then change it's style
  90. if($(".group-setting").children().text().replace(/\s+/g, "")!=""){
  91. $(".group-setting").html('\
  92. <ul class="btn-group btn-group-sm">\
  93. <a href="/mine/" class="btn btn-sm btn-outline-secondary ">修改设定</a>\
  94. <a href="javascript:void(0);" onclick="if (confirm(\'你确定要退出小组吗?\')) api.leaveGroup(9,null,local.redirect);" class="btn btn-sm btn-danger">退出小组</a>\
  95. </ul>').addClass('mt-3 mb-0');
  96. }
  97.  
  98. // Site index page fixing
  99. $(".row").children('p').addClass('col-md-12 alert alert-info');
  100. $(".row").children('p').each(function(){if($(this).text().replace(/\s+/g, "")=="")$(this).hide();}); // remove extra spaces
  101. $(".user-group,.my-group-contest").addClass("row");
  102. $(".recently-submit,.my-contest-list").addClass('col-md-10');
  103.  
  104. // Alerts
  105. $(".notification").attr('class','alert alert-warning');
  106. $(".contest-description").attr('class','alert alert-info');
  107. $(".notice").attr('class','alert alert-danger');
  108.  
  109. // Change top menu
  110. var tabs = $("#topMenu ul")
  111. tabs.addClass('nav nav-tabs');
  112. tabs.children('li').addClass('nav-item');
  113. $('.nav a').addClass('nav-link');
  114. $(".current-show").children().addClass('nav-link active');
  115. tabs.children('li').removeClass('current-show');
  116. $("#topMenu").addClass('col-md-12 mb-3 mt-2').removeAttr('id');
  117.  
  118. // Change bottom menu
  119. tabs = $(".bottomMenu");
  120. tabs.addClass('pagination');
  121. tabs.children('li').addClass('page-item');
  122. $('.pagination a').addClass('page-link');
  123. $(".current-show").addClass('active');
  124. tabs.children('li').removeClass('current-show');
  125.  
  126. // Change tables' style
  127. $("table").addClass('table table-sm table-hover table-responsive');
  128. $("#main,#contestStatistics,#problemStatus").children("table").wrapAll("<div class='row justify-content-center'><div class='col-auto'></div></div>");
  129. $(".practice-info table,#problemsList table,.recently-submit table").removeClass('table-responsive');
  130. $("thead tr td").replaceWith(function () {
  131. return $("<th />").append($(this).contents());
  132. });
  133. $("table thead").addClass('thead-light text-center');
  134. $(".practice-info table thead").removeClass('text-center');
  135. $("table td.accepted,table td.submissions,table td.code-length").css('min-width','5em');
  136. $("table td.title").css('min-width','15em');
  137.  
  138.  
  139. // Remove too long text
  140. $("td.class-name,td.className").each(function() {
  141. if ($(this).text().length > 10 && $(this).width() < 150) {
  142. $(this).attr('title',$(this).text());
  143. $(this).html($(this).text().replace(/\s+/g, "").substr(0, 10) + "...")
  144. }
  145. })
  146.  
  147. // Change searching form
  148. $(".status-search form").addClass('form-inline justify-content-center');
  149. $(".status-query-params").addClass('row');
  150. $(".status-query-params input,.status-query-params select").addClass('form-control form-control-sm');
  151. $(".status-query-params button").addClass('btn-sm');
  152.  
  153. // Change page bar
  154. var pageBar = $(".page-bar");
  155. if(pageBar.length > 0){
  156. pageBar.removeClass('page-bar');
  157. pageBar = pageBar.children('.pages').attr('class','pagination pagination-sm justify-content-center');
  158. pageBar.children().each(function(){
  159. if($(this).hasClass('current')){
  160. $(this).attr('class','page-link');
  161. $(this).wrapAll('<span class="page-item active"></span>');
  162. } else if ($(this).is('a')){
  163. $(this).attr('class','page-link');
  164. $(this).wrapAll('<span class="page-item"></span>');
  165. } else {
  166. $(this).attr('class','page-link');
  167. $(this).wrapAll('<span class="page-item disabled"></span>');
  168. }
  169. });
  170. // If it's a contest page, add pagigation in the top
  171. if($(".timeBar").length > 0) {
  172. $(".timeBar").after(pageBar.clone());
  173. }
  174. }
  175.  
  176. var abbr = {
  177. "Accepted": "AC",
  178. "Wrong Answer": "WA",
  179. "Time Limit Exceeded": "TLE",
  180. "Memory Limit Exceeded": "MLE",
  181. "Output Limit Exceeded": "OLE",
  182. "Runtime Error": "RE",
  183. "Compile Error": "CE",
  184. "Presentation Error": "PE",
  185. "Waiting": "Waiting"
  186. };
  187.  
  188. var color = {
  189. "Accepted": "#52C41A",
  190. "Wrong Answer": "#E74C3C",
  191. "Presentation Error": "#00A497",
  192. "Time Limit Exceeded": "#052242",
  193. "Memory Limit Exceeded": "#052242",
  194. "Output Limit Exceeded": "#E74C3C",
  195. "Runtime Error": "#9D3DCF",
  196. "Compile Error": "#FADB14",
  197. "Waiting": "#14558F"
  198. }
  199.  
  200. // Change solution's style
  201. if(/^\/[^\/]+\/solution\/\d+\/?$/.test(window.location.pathname)) {
  202.  
  203. var result = $('.compile-status a').text();
  204. $('.compile-status a').remove();
  205.  
  206. var memory = $('.compile-info dl dd:eq(3)').text();
  207. var time = $('.compile-info dl dd:eq(4)').text();
  208.  
  209. var detail = result == "Compile Error" || result == "Waiting" ? "" : time +"/" + memory;
  210.  
  211. var newStatus = "\
  212. <div class='beautiful-status' title='" + result + "'>\
  213. " + abbr[result] + "\
  214. <div style='font-size:11px;'>" + detail + "</div>\
  215. </div>";
  216.  
  217. $('.compile-status').append(newStatus);
  218.  
  219. $('.beautiful-status').css({
  220. "background-color": color[result],
  221. "height": "100px",
  222. "width": "100px",
  223. "margin-top": "20px",
  224. "display": "flex",
  225. "align-items": "center",
  226. "justify-content": "center",
  227. "flex-direction": "column",
  228. "color": "white",
  229. "font-size": "24px",
  230. "font-family": "-apple-system, BlinkMacSystemFont, 'San Francisco', 'Helvetica Neue', 'Noto Sans CJK SC', 'Noto Sans CJK', 'Source Han Sans', 'PingFang SC', 'Microsoft YaHei', sans-serif"
  231. });
  232. }
  233.  
  234. // Change code's font
  235. if(/^\/[^\/]+\/[^\/]+\/submit\/?$/.test(window.location.pathname)) {
  236. $("textarea#source").css({
  237. "font-family": "Monaco, Menlo, 'Ubuntu Mono', Consolas, source-code-pro, monospace",
  238. "width": "100%"
  239. })
  240. // $("#submit").removeClass('col-md-9');
  241. $("#submit dt:eq(1)").text("语言");
  242. $("#submit form textarea").addClass('form-control');
  243. $("#main").width("100%");
  244. $(".submit-button").removeClass('btn-secondary').addClass('btn-primary');
  245. }
  246. $("pre,span.sh_string").css({
  247. "font-family": "Monaco, Menlo, 'Ubuntu Mono', Consolas, source-code-pro, monospace"
  248. })
  249.  
  250. // Change question('clarify')'s style
  251. if(/^\/[^\/]+\/clarify(\/[^\/]*\/?)?$/.test(window.location.pathname) || /^\/mine\/?$/.test(window.location.pathname)) {
  252. $("#main form").addClass('form');
  253. $("#main form textarea,#main form :text").addClass("form-control").width("90%");
  254. $("#main form :text").height("auto");
  255. }
  256.  
  257. // Change ranking style
  258. if(/^\/[^\/]+\/ranking\/?$/.test(window.location.pathname)) {
  259. var allData = $("td.alpha");
  260. for(var i = 0; i < allData.length; i++) {
  261. var text = allData.eq(i).html();
  262. if(text.indexOf("<a") != -1) continue;
  263. if(text.indexOf(":") != -1 ) { // If passed
  264. var res = '&#8730;';
  265. if(text.indexOf('<br>') != -1) {
  266. res = "&#8730;" + text.split('<br>')[1];
  267. }
  268. allData.eq(i).html(res);
  269. allData.eq(i).css({
  270. "background-color": "#dff0d890",
  271. "color": "#3c763d"
  272. });
  273. } else if(text.indexOf('(-') != -1) { // Or not passed, but tried
  274. res = text.split('<br>')[1];
  275. allData.eq(i).html(res);
  276. allData.eq(i).css({
  277. "background-color": "#f2dede90",
  278. "color": "#a94442"
  279. });
  280. }
  281. }
  282.  
  283. $(document).ready(function(){
  284. // If too wide, add scrolling event
  285. if($('table')[0].scrollWidth > $('table').width()) {
  286. $('table').attr('id','scroll-horizontally');
  287. (function(){
  288. function scrollHorizontally(e) {
  289. e = window.event || e;
  290. var delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail)));
  291. document.getElementById('scroll-horizontally').scrollLeft -= (delta * 60);
  292. e.preventDefault();
  293. }
  294. if (document.getElementById('scroll-horizontally').addEventListener) {
  295. // IE9, Chrome, Safari, Opera
  296. document.getElementById('scroll-horizontally').addEventListener("mousewheel", scrollHorizontally, false);
  297. // Firefox
  298. document.getElementById('scroll-horizontally').addEventListener("DOMMouseScroll", scrollHorizontally, false);
  299. } else {
  300. // IE 6/7/8
  301. document.getElementById('scroll-horizontally').attachEvent("onmousewheel", scrollHorizontally);
  302. }
  303. })();
  304. }
  305. });
  306.  
  307. }
  308.  
  309. // Change other status's style
  310. var otherResult = $('.result-wrong,.result-ce,.result-right,.result-pending');
  311. otherResult.css({
  312. "padding": "3px 6px",
  313. "color": "white",
  314. "border-radius": "2px",
  315. "font-style": "normal"
  316. });
  317. for(var i = 0; i < otherResult.length; i++) {
  318. var res = otherResult.eq(i).text();
  319. otherResult.eq(i).text(abbr[res]);
  320. otherResult.eq(i).attr('title',res);
  321. otherResult.eq(i).css({
  322. "background-color": color[res],
  323. });
  324. }
  325. $('.result').css('width','auto');
  326.  
  327.  
  328. // Change finish ratio's style
  329. function getColorByRatio(ratio){
  330. var one = (255+255) / 100;
  331. var r = 0;
  332. var g = 0;
  333. var b = 0;
  334. if (ratio < 50) {
  335. r = one * ratio;
  336. g = 255;
  337. }
  338. if (ratio >= 50) {
  339. g = 255 - ((ratio - 50 ) * one) ;
  340. r = 255;
  341. }
  342. r = parseInt(r);
  343. g = parseInt(g);
  344. b = parseInt(b);
  345. return "rgba(" + r + "," + g + "," + b + ",0.5)";
  346. }
  347. var ratios = $('.ratio');
  348. for(var i = 0; i < ratios.length; i++) {
  349. var value = parseInt(ratios.eq(i).text().replace('%',''));
  350. if (!isNaN(value)) {
  351. ratios.eq(i).css({'background-color':getColorByRatio(100 - value),'min-width':'4em'});
  352. }
  353. }
  354.  
  355. // Change time bar
  356. if($('.timeBar').length > 0) {
  357. var startTime = new Date($(".start-time-dd").text());
  358. var endTime = new Date($(".end-time-dd").text());
  359. var currentState = $(".current-time").text();
  360. function formatDateTime(date) {
  361. var y = date.getFullYear();
  362. var m = date.getMonth() + 1;
  363. m = m < 10 ? ('0' + m) : m;
  364. var d = date.getDate();
  365. d = d < 10 ? ('0' + d) : d;
  366. var h = date.getHours();
  367. h=h < 10 ? ('0' + h) : h;
  368. var minute = date.getMinutes();
  369. minute = minute < 10 ? ('0' + minute) : minute;
  370. var second=date.getSeconds();
  371. second=second < 10 ? ('0' + second) : second;
  372. return y + '-' + m + '-' + d+' '+h+':'+minute+':'+second;
  373. };
  374. var newTime = '\
  375. <div class="row">\
  376. <div class="col-md-3 text-left"><small>开始时间</small><br><b>'+ formatDateTime(startTime) +'</b></div>\
  377. <div class="col-md-6 text-center"><b>' + currentState + '</b><p><small id="currentTime"></small></p></div>\
  378. <div class="col-md-3 text-right"><small>结束时间</small><br><b>'+ formatDateTime(endTime) + '</b></div>\
  379. </div>';
  380. $('.timeBar').append(newTime).addClass('mt-4 mb-4');
  381. $('.current-contest-info,.past-contest-info').remove();
  382. $('.timeBar').append('<p style="display:none;" id="timeclock"></p>');
  383. if(new Date() < endTime && new Date() > startTime){
  384. var total = endTime - startTime;
  385. var progress = '\
  386. <div class="progress">\
  387. <div class="progress-bar progress-bar-striped progress-bar-animated" id="timeProgressBar"\
  388. role="progressbar" style="width: 0%" aria-valuenow="25" aria-valuemin="0" aria-valuemax="100">\
  389. </div>\
  390. </div>';
  391. $('.timeBar').append(progress);
  392. function updateValue(){
  393. var currentTime = new Date();
  394. var percent = (currentTime - startTime) * 100 / total;
  395. $("#timeProgressBar").attr({'style':'width:' + percent + '%','aria-valuenow':percent + ''});
  396. $("#currentTime").text(formatDateTime(currentTime));
  397. }
  398. updateValue();
  399. setInterval(updateValue,1000);
  400. }
  401. }
  402.  
  403. // Change those old icon to unicode character
  404. var solvedIcon = $('.solved');
  405. for(var i = 0; i < solvedIcon.length; i++) {
  406. var html = solvedIcon.eq(i).html();
  407. if(html.indexOf('accepted') != -1) {
  408. solvedIcon.eq(i).html("<span style='color:green;font-weight:bold;'>&#8730;</span>");
  409. }
  410. else if(html.indexOf('wrong') != -1) {
  411. solvedIcon.eq(i).html("<span style='color:red;;font-weight:bold;'>&#215;</span>");
  412. }
  413. }
  414.  
  415. // Change login page
  416. var loginForm = $("#main form[action='/api/auth/login/']");
  417. if(loginForm.length>0) {
  418. loginForm.parent().removeClass('col-md-8').addClass('col-md-12');
  419. loginForm.html('\
  420. <style>\
  421. .form-signin {\
  422. width: 100%;\
  423. max-width: 330px;\
  424. padding: 15px;\
  425. margin: auto;\
  426. }\
  427. .form-signin .checkbox {\
  428. font-weight: 400;\
  429. }\
  430. .form-signin .form-control {\
  431. position: relative;\
  432. box-sizing: border-box;\
  433. height: auto;\
  434. padding: 10px;\
  435. font-size: 16px;\
  436. }\
  437. .form-signin .form-control:focus {\
  438. z-ind ex: 2;\
  439. }\
  440. .form-signin input[type="text"] {\
  441. margin-bottom: -1px;\
  442. border-bottom-right-radius: 0;\
  443. border-bottom-left-radius: 0;\
  444. }\
  445. .form-signin input[type="password"] {\
  446. margin-bottom: 10px;\
  447. border-top-left-radius: 0;\
  448. border-top-right-radius: 0;\
  449. }\
  450. </style>\
  451. <form action="/api/auth/login/" method="post" onsubmit="$(this).ajaxSubmit({dataType:\'json\',success:local.redirect,\'target\':\'#result\'}).find(\':submit\').each(function(){this.disabled=true;});return false;" class="form-signin">\
  452. <input type="hidden" name="redirectUrl" value="">\
  453. <div class="login-message"></div>\
  454. <label for="email" class="sr-only">邮箱地址</label>\
  455. <input id="email" type="text" name="email" size="20" onfocus="this.select();" class="form-control" placeholder="邮箱地址">\
  456. <label for="password" class="sr-only">密码</label>\
  457. <input id="password" type="password" name="password" size="20" class="form-control" placeholder="密码">\
  458. <button type="submit" class="btn btn-block btn-primary mt-3">登入</button>\
  459. <div class="d-flex justify-content-between mt-3">\
  460. <p><a href="/register/">点此注册(不可用)</a></p>\
  461. <p><a href="http://openjudge.cn/auth/forget/">忘记密码?</a></p>\
  462. </div>\
  463. </form>');
  464. }
  465.  
  466. // Change register style
  467. if(/^\/register\/?$/.test(window.location.pathname)) {
  468. var originalStyle = $('#sitePagebody .row').children('style').text();
  469. $('#sitePagebody .row').children('style').text(originalStyle.replace(/\.btn\s*\{[^}]*\}/,''));
  470. $('#sitePagebody .row').addClass('justify-content-center');
  471. $('#main').removeClass('col-md-10').addClass('col-md-4');
  472. $('form dd,form dt').remove();
  473. $('form').prepend('\
  474. <div class="mb-3">\
  475. <label for="regEmail">Email地址</label>\
  476. <input id="regEmail" type="text" name="user_email" class="form-control">\
  477. </div>\
  478. <div class="mb-3">\
  479. <label for="regName">用户名</label>\
  480. <input id="regName" type="text" name="user_name" class="form-control">\
  481. </div>\
  482. <div class="mb-3">\
  483. <label for="regPasswd">密码</label>\
  484. <input id="regPasswd" type="password" name="user_passwd" class="form-control">\
  485. </div>\
  486. <div class="mb-3">\
  487. <label for="regPasswd2">确认密码</label>\
  488. <input id="regPasswd2" type="password" name="user_passwd2" class="form-control">\
  489. </div>\
  490. ');
  491. $('form').append('\
  492. <p id="wait" class="hide">正在加载验证码......</p>\
  493. <p id="notice" class="hide">请先拖动验证码到相应位置</p>\
  494. <button type="submit" class="mt-3 btn btn-block btn-primary btn-lg">注册(不可用)</button>');
  495. }
  496. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址