// locale uojLocaleData = { "username": { "en": "Username", "zh-cn": "用户名" }, "contests::total score": { "en": "Score", "zh-cn": "总分" }, "contests::n participants": { "en": function(n) { return n + " participant" + (n <= 1 ? '' : 's'); }, "zh-cn": function(n) { return "共 " + n + " 名参赛者"; } }, "click-zan::good": { "en": "Good", "zh-cn": "好评" }, "click-zan::bad": { "en": "Bad", "zh-cn": "差评" }, "editor::language": { "en": "Language", "zh-cn": "语言" }, "editor::browse": { "en": "Browse", "zh-cn": "浏览" }, "editor::upload source": { "en": "Source", "zh-cn": "来源" }, "editor::upload by editor": { "en": "Editor", "zh-cn": "编辑器" }, "editor::upload from local": { "en": "Local file", "zh-cn": "本地文件", }, "editor::edit": { "en": "Edit", "zh-cn": "编辑", }, "editor::preview": { "en": "Preview", "zh-cn": "预览", }, }; function uojLocale(name) { locale = $.cookie('uoj_locale'); if (uojLocaleData[name] === undefined) { return ''; } if (uojLocaleData[name][locale] === undefined) { locale = 'zh-cn'; } val = uojLocaleData[name][locale]; if (!$.isFunction(val)) { return val; } else { var args = []; for (var i = 1; i < arguments.length; i++) { args.push(arguments[i]); } return val.apply(this, args); } } // utility function strToDate(str) { var a = str.split(/[^0-9]/); return new Date( parseInt(a[0]), parseInt(a[1]) - 1, parseInt(a[2]), parseInt(a[3]), parseInt(a[4]), parseInt(a[5]), 0); } function dateToStr(date) { return date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate() + ' ' + date.getHours() + ':' + date.getMinutes() + ':' + date.getSeconds(); } function toFilledStr(o, f, l) { var s = o.toString(); while (s.length < l) { s = f.toString() + s; } return s; } function getPenaltyTimeStr(x) { var ss = toFilledStr(x % 60, '0', 2); x = Math.floor(x / 60); var mm = toFilledStr(x % 60, '0', 2); x = Math.floor(x / 60); var hh = x.toString(); return hh + ':' + mm + ':' + ss; } function htmlspecialchars(str) { var s = ""; if (str.length == 0) return ""; s = str.replace(/&/g, "&"); s = s.replace(//g, ">"); s = s.replace(/"/g, """); return s; } function getColOfScore(score) { if (score == 0) { return ColorConverter.toStr(ColorConverter.toRGB(new HSV(0, 76, 86))); } else if (score == 100) { return ColorConverter.toStr(ColorConverter.toRGB(new HSV(130, 89, 72))); } else { return ColorConverter.toStr(ColorConverter.toRGB(new HSV(30 + score * 60 / 100, 83, 89))); } } function getUserLink(username, realname, color) { if (!username) { return ''; } var text = username; var className = 'uoj-username'; if (username.charAt(0) == '@') { username = username.substr(1); } if (realname) { text = text + ' (' + realname + ')'; } if (color) { className += ' uoj-username-' + color; } return '' + text + ''; } function getUserSpan(username, realname, color) { if (!username) { return ''; } var text = username; var className = 'uoj-username'; if (username.charAt(0) == '@') { username = username.substr(1); } if (realname) { text = text + ' (' + realname + ')'; } if (color) { className += ' uoj-username-' + color; } return '' + text + ''; } function replaceWithHighlightUsername() { var username = $(this).text(); var realname = $(this).data("realname"); var color = $(this).data("color"); if ($(this).data("link") != 0) { $(this).replaceWith(getUserLink(username, realname, color)); } else { $(this).replaceWith(getUserSpan(username, realname, color)); } } $.fn.uoj_honor = function() { return this.each(function() { var honor = $(this).text(); var realname = $(this).data("realname"); if (realname) { honor = honor + ' (' + realname + ')'; } $(this).css('color', '#007bff').html(honor); }); } function getClickZanBlock(type, id, cnt, val) { return '
'; } function showErrorHelp(name, err) { if (err) { $('#div-' + name).addClass('has-validation has-error'); $('#div-' + name).addClass('is-invalid'); $('#input-' + name).addClass('is-invalid'); $('#help-' + name).text(err); return false; } else { $('#div-' + name).removeClass('has-validation has-error'); $('#div-' + name).removeClass('is-invalid'); $('#input-' + name).removeClass('is-invalid'); $('#help-' + name).text(''); return true; } } function getFormErrorAndShowHelp(name, val) { var err = val($('#input-' + name).val()); return showErrorHelp(name, err); } function validateSettingPassword(str) { if (str.length < 6) { return '密码长度不应小于6。'; } else if (! /^[!-~]+$/.test(str)) { return '密码应只包含可见ASCII字符。'; } else if (str != $('#input-confirm_password').val()) { return '两次输入的密码不一致。'; } else { return ''; } } function validatePassword(str) { if (str.length < 6) { return '密码长度不应小于6。'; } else if (! /^[!-~]+$/.test(str)) { return '密码应只包含可见ASCII字符。'; } else { return ''; } } function validateEmail(str) { if (str.length > 50) { return '电子邮箱地址太长。'; } else if (! /^(.+)@(.+)$/.test(str)) { return '电子邮箱地址非法。'; } else { return ''; } } function validateUsername(str) { if (str.length == 0) { return '用户名不能为空。'; } else if (! /^[a-zA-Z0-9_]+$/.test(str)) { return '用户名应只包含大小写英文字母、数字和下划线。'; } else { return ''; } } function validateQQ(str) { if (str.length < 5) { return 'QQ的长度不应小于5。'; } else if (str.length > 15) { return 'QQ的长度不应大于15。'; } else if (/\D/.test(str)) { return 'QQ应只包含0~9的数字。'; } else { return ''; } } function validateMotto(str) { if (str.length > 1024) { return '不能超过 1024 个字符。'; } else { return ''; } } // tags $.fn.uoj_problem_tag = function() { return this.each(function() { $(this).attr('href', uojHome + '/problems?tag=' + encodeURIComponent($(this).text())); }); } $.fn.uoj_list_tag = function() { return this.each(function() { $(this).attr('href', uojHome + '/lists?tag=' + encodeURIComponent($(this).text())); }); } $.fn.uoj_blog_tag = function() { return this.each(function() { $(this).attr('href', uojBlogUrl + '/archive?tag=' + encodeURIComponent($(this).text())); }); } // click zan function click_zan(zan_id, zan_type, zan_delta, node) { var loading_node = $('
loading...
'); $(node).replaceWith(loading_node); $.post(zan_link + '/click-zan', { id : zan_id, delta : zan_delta, type : zan_type, }, function(ret) { $(loading_node).replaceWith($(ret).click_zan_block()); }).fail(function() { $(loading_node).replaceWith('
failed
'); }); } $.fn.click_zan_block = function() { return this.each(function() { var id = $(this).data('id'); var type = $(this).data('type'); var val = parseInt($(this).data('val')); var cnt = parseInt($(this).data('cnt')); var rendered = $(this).attr('data-click-zan-rendered'); if (rendered == 'true') { return; } if (isNaN(cnt)) { return; } var up_icon_html = ''; var down_icon_html = ''; if (val == 1) { $(this).addClass('uoj-click-zan-block-cur-up'); up_icon_html = ''; } else if (val == 0) { $(this).addClass('uoj-click-zan-block-cur-zero'); } else if (val == -1) { $(this).addClass('uoj-click-zan-block-cur-down'); down_icon_html = ''; } else { return; } if (cnt > 0) { $(this).addClass('uoj-click-zan-block-positive'); } else if (cnt == 0) { $(this).addClass('uoj-click-zan-block-neutral'); } else { $(this).addClass('uoj-click-zan-block-negative'); } var node = this; var up_node = $(''+up_icon_html+uojLocale('click-zan::good')+'').click(function(e) { e.preventDefault(); click_zan(id, type, 1, node); }); var down_node = $(''+down_icon_html+uojLocale('click-zan::bad')+'').click(function(e) { e.preventDefault(); click_zan(id, type, -1, node); }); var display_cnt = cnt > 0 ? '+' + cnt : cnt; if (cnt < 0) display_cnt = '-?'; $(this) .attr('data-click-zan-rendered', 'true') .append(up_node) .append(down_node) .append($('[' + display_cnt + ']')); }); } // count down function getCountdownStr(t, config) { var x = Math.floor(t); var ss = toFilledStr(x % 60, '0', 2); x = Math.floor(x / 60); var mm = toFilledStr(x % 60, '0', 2); x = Math.floor(x / 60); var hh = x.toString(); var style = ''; if (config.font_size) { style += 'font-size: ' + config.font_size + ';'; } var res = ''; res += '' + hh + ''; res += ':'; res += '' + mm + ''; res += ':'; res += '' + ss + ''; res += ''; return res; } $.fn.countdown = function(rest, config = {}) { return this.each(function() { var start = new Date().getTime(); var cur_rest = rest != undefined ? rest : parseInt($(this).data('rest')); config = $.merge(config, JSON.parse($(this).data('config') || '{}')); var cur = this; var countdown = function() { var passed = Math.floor((new Date().getTime() - start) / 1000); if (passed >= cur_rest) { $(cur).html(getCountdownStr(0, config)); if (config.callback != undefined) { callback(); } } else { $(cur).html(getCountdownStr(cur_rest - passed, config)); setTimeout(countdown, 500); } } countdown(); }); }; // update_judgement_status update_judgement_status_list = [] function update_judgement_status_details(id) { update_judgement_status_list.push(id); }; $(document).ready(function() { function update() { $.get("/api/submission/submission_status_details", { get: update_judgement_status_list, }, function(data) { for (var i = 0; i < update_judgement_status_list.length; i++) { $("#status_details_" + update_judgement_status_list[i]).html(data[i].html); if (data[i].judged) { location.reload(); } } }, 'json').always( function() { setTimeout(update, 500); } ); } if (update_judgement_status_list.length > 0) { setTimeout(update, 500); } }); // highlight $.fn.uoj_highlight = function() { return $(this).each(function() { $(this).find("span.uoj-username, span[data-uoj-username]").each(replaceWithHighlightUsername); $(this).find(".uoj-honor").uoj_honor(); $(this).find(".uoj-score").each(function() { var score = parseFloat($(this).text()); var maxscore = parseFloat($(this).data('max')); if (isNaN(score)) { return; } if (isNaN(maxscore)) { $(this).css("color", getColOfScore(score)); } else { $(this).css("color", getColOfScore(score / maxscore * 100)); } }); $(this).find(".uoj-status").each(function() { var success = parseInt($(this).data("success")); if(isNaN(success)){ return; } if (success == 1) { $(this).css("color", ColorConverter.toStr(ColorConverter.toRGB(new HSV(120, 100, 80)))); } else { $(this).css("color", ColorConverter.toStr(ColorConverter.toRGB(new HSV(0, 100, 100)))); } }); $(this).find(".uoj-problem-tag").uoj_problem_tag(); $(this).find(".uoj-list-tag").uoj_list_tag(); $(this).find(".uoj-blog-tag").uoj_blog_tag(); $(this).find(".uoj-click-zan-block").click_zan_block(); $(this).find(".countdown").countdown(); $(this).find(".uoj-readmore").readmore({ moreLink: 'more...', lessLink: 'close', }); }); }; $(document).ready(function() { $('body').uoj_highlight(); }); // contest notice function checkNotice(lastTime) { $.post(uojHome + '/check-notice', { last_time : lastTime }, function(data) { if (data === null) { return; } setTimeout(function() { checkNotice(data.time); }, 60000); for (var i = 0; i < data.msg.length; i++) { alert(data.msg[i]); } }, 'json' ).fail(function() { setTimeout(function() { checkNotice(lastTime); }, 60000); }); } // long table $.fn.long_table = function(data, cur_page, header_row, get_row_str, config) { return this.each(function() { var table_div = this; $(table_div).html(''); var page_len = config.page_len != undefined ? config.page_len : 10; if (!config.echo_full) { var n_rows = data.length; var n_pages = Math.max(Math.ceil(n_rows / page_len), 1); if (cur_page == undefined) { cur_page = 1; } if (cur_page < 1) { cur_page = 1; } else if (cur_page > n_pages) { cur_page = n_pages; } var cur_start = (cur_page - 1) * page_len; } else { var n_rows = data.length; var n_pages = 1; cur_page = 1; var cur_start = (cur_page - 1) * page_len; } var div_classes = config.div_classes != undefined ? config.div_classes : ['table-responsive']; var table_classes = config.table_classes != undefined ? config.table_classes : ['table', 'table-bordered', 'table-hover', 'table-striped', 'table-text-center']; var now_cnt = 0; var tbody = $('') for (var i = 0; i < page_len && cur_start + i < n_rows; i++) { now_cnt++; if (config.get_row_index) { tbody.append(get_row_str(data[cur_start + i], cur_start + i)); } else { tbody.append(get_row_str(data[cur_start + i])); } } if (now_cnt == 0) { tbody.append('无'); } $(table_div).append( $('
') .append(typeof config.print_before_table === 'function' ? config.print_before_table() : '') .append( $('') .append($('').append(header_row)) .append(tbody) ) .append(typeof config.print_after_table === 'function' ? config.print_after_table() : '') ); var get_page_li = function(p, h) { if (p == -1) { return $('
  • ').addClass('disabled').append($('').append(h)); } var li = $('
  • '); if (p == cur_page) { li.addClass('active'); } li.append( $('').attr('href', '#' + table_div.id).append(h).click(function(e) { if (config.prevent_focus_on_click) { e.preventDefault(); } $(table_div).long_table(data, p, header_row, get_row_str, config); }) ); return li; }; var get_pagination = function() { var pagination = $(''); if (cur_page > 1) { pagination.append(get_page_li(1, '')); pagination.append(get_page_li(cur_page - 1, '')); } else { pagination.append(get_page_li(-1, '')); pagination.append(get_page_li(-1, '')); } var max_extend = config.max_extend != undefined ? config.max_extend : 5; for (var i = Math.max(cur_page - max_extend, 1); i <= Math.min(cur_page + max_extend, n_pages); i++) { pagination.append(get_page_li(i, i.toString())); } if (cur_page < n_pages) { pagination.append(get_page_li(cur_page + 1, '')); pagination.append(get_page_li(n_pages, '')); } else { pagination.append(get_page_li(-1, '')); pagination.append(get_page_li(-1, '')); } return pagination; } if (n_pages > 1) { $(table_div).append($('
    ').append(get_pagination())); if (config.top_pagination) { $(table_div).prepend($('
    ').append(get_pagination())); } } }); }; // monaco editor function require_monaco(config, callback) { window.require = { paths: { vs: '/js/monaco-editor/min/vs', }, "vs/nls": { availableLanguages: { '*': 'zh-cn', }, }, }; $LAB.script('/js/monaco-editor/min/vs/loader.js').wait() .script('/js/monaco-editor/min/vs/editor/editor.main.nls.js').wait() .script('/js/monaco-editor/min/vs/editor/editor.main.nls.zh-cn.js').wait() .script('/js/monaco-editor/min/vs/editor/editor.main.js').wait(function() { $LAB.script('/js/monaco-themes.js').wait(function() { update_monaco_theme(getPreferredTheme()); if (config.markdown) { $LAB.script('/js/blog-editor/monaco-markdown.js').wait(callback); } else { callback(); } }); }); } function update_monaco_theme(theme) { if (!('monaco' in window)) return; if (theme == 'dark') { monaco.editor.setTheme('github-dark'); } else { monaco.editor.setTheme('github'); } } function get_monaco_mode(lang) { switch (lang) { case 'C++': case 'C++11': case 'C++17': case 'C++20': case 'C++98': case 'C++03': return 'cpp'; case 'C': return 'c'; case 'Python2': case 'Python2.7': case 'Python3': return 'python'; case 'Pascal': return 'pascal'; case 'Java8': case 'Java11': case 'Java17': return 'java'; case 'text': return 'text'; default: return 'text'; } } // auto save function autosave_locally_monaco(interval, name, monaco_instance) { if (typeof(Storage) === "undefined") { console.log('autosave_locally_monaco: Sorry! No Web Storage support..'); return; } var url = window.location.href; var hp = url.indexOf('#'); var uri = hp == -1 ? url : url.substring(0, hp); var full_name = name + '@' + uri; monaco_instance.getModel().setValue(localStorage.getItem(full_name)); var save = function() { localStorage.setItem(full_name, monaco_instance.getModel().getValue()); setTimeout(save, interval); }; setTimeout(save, interval); } // source code form group $.fn.source_code_form_group = function(name, text, langs_options_html) { return this.each(function() { var input_upload_type_editor_id = 'input-' + name + '_upload_type_editor'; var input_upload_type_file_id = 'input-' + name + '_upload_type_file'; var input_upload_type_name = name + '_upload_type'; var input_language_id = 'input-' + name + '_language'; var input_language_name = name + '_language'; var input_editor_id = 'input-' + name + '_editor'; var input_editor_name = name + '_editor'; var input_file_id = 'input-' + name + '_file'; var input_file_name = name + '_file'; var spinner_id = 'spinner-' + name + '_editor'; var div_help_language_id = 'div-help-' + name + '_language'; var div_editor_id = 'div-' + name + '_editor'; var div_file_id = 'div-' + name + '_file'; var help_file_id = 'help-' + name + '_file'; var input_language = $(''); var input_upload_type_file = $(''); var input_file = $(''); var div_editor = $('
    ') .append( $('
    ') .append('
    ') ); var div_file = $('
    ') .append(input_file) .append($('')) var div_help_language = $('
    '); var show_help_lang = function() { if ($(this).val().startsWith('Java')) { div_help_language.text('注意:Java 程序源代码中不应指定所在的 package。我们会在源代码中找到第一个被定义的类并以它的 main 函数为程序入口点。'); } else { div_help_language.text(''); } }; var monaco_editor_instance = null; var monaco_editor_init = function() { require_monaco({}, function() { if (monaco_editor_instance != null) { return; } $(div_editor).html(''); var mode = get_monaco_mode(input_language.val()); monaco_editor_instance = monaco.editor.create(div_editor[0], { language: mode, automaticLayout: true, fontSize: "16px", }); $('#' + spinner_id).css('display', 'none !important'); $(div_editor).addClass('border overflow-hidden').show(); autosave_locally_monaco(2000, name, monaco_editor_instance); $('#' + input_editor_id).val(monaco_editor_instance.getModel().getValue()); monaco_editor_instance.onDidChangeModelContent(function () { $('#' + input_editor_id).val(monaco_editor_instance.getModel().getValue()); }); input_language.change(function() { monaco.editor.setModelLanguage(monaco_editor_instance.getModel(), get_monaco_mode(input_language.val())); }); }); } var save_prefer_upload_type = function(type) { $.cookie('uoj_source_code_form_group_preferred_upload_type', type, { expires: 7, path: '/' }); }; var prefer_upload_type = $.cookie('uoj_source_code_form_group_preferred_upload_type'); if (prefer_upload_type === null) { prefer_upload_type = 'editor'; } if (prefer_upload_type == 'file') { input_upload_type_file[0].checked = true; div_editor.css('display', 'none'); } else { input_upload_type_editor[0].checked = true; div_file.css('display', 'none'); } input_language.each(show_help_lang); input_language.change(show_help_lang); input_upload_type_editor.click(function() { div_editor.show('fast'); div_file.hide('fast'); save_prefer_upload_type('editor'); }); input_upload_type_file.click(function() { div_file.show('fast'); div_editor.hide('fast'); save_prefer_upload_type('file'); }); $(this).append( $('
    ') .append($('
    ' + text + '
    ')) .append( $('
    ') .append($('
    ').append('')) .append($('
    ').append(input_language)) ) .append($('
    ') .append(uojLocale('editor::upload source') + ': ') .append($('
    ') .append($('
    ' + data.html + '
    '); $(div_result).empty(); $(div_result).append(judge_status); if (data.judged) { var judge_result = $(data.result); judge_result.css('display', 'none'); $(div_result).append(judge_result); judge_status.hide(500); judge_result.slideDown(500); can_next = false; } } }, 'json') .always(function() { if (can_next) { setTimeout(update, 500); } }); }; setTimeout(update, 500); } // markdown input editor $.fn.markdown_input_editor = function(name, type, text) { return this.each(function() { var input_editor_name = name; var input_editor_id = 'input-' + name + '_editor'; var spinner_id = 'spinner-' + name + '_editor'; var div_editor_id = 'div-' + name + '_editor'; var div_preview_id = 'div-' + name + '_preview'; var btn_editor = $('') .click(function () { navigator.clipboard .writeText($(thisEl).text()) .then(() => { $(this).html(''); setTimeout(() => { $(this).html(''); }, 1000); }) .catch(() => { $(this).html(''); setTimeout(() => { $(this).html(''); }, 1000); }); }) .append('') ) ); }); }); /** * Fallback user avatar */ $(document).ready(function() { $('img.uoj-user-avatar').on('error', function() { // same as gravatar d=mm $(this).attr('src', 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA4NzIgODcyIj48cGF0aCBmaWxsPSIjZmZmIiBkPSJNMCAwaDg3MnY4NzJIMHoiLz48cGF0aCBmaWxsPSIjYzVjNWM1IiBkPSJNMCAwdjg3MmgxMTBhMzI3IDM4MyAwIDAxMjM2LTM0NSAxOTUgMTk1IDAgMDEtMTA0LTE3MiAxOTUgMTk1IDAgMDExOTUtMTk1IDE5NSAxOTUgMCAwMTE5NSAxOTUgMTk1IDE5NSAwIDAxLTEwNiAxNzMgMzI3IDM4MyAwIDAxMjM2IDM0NGgxMTBWMHoiLz48L3N2Zz4='); }); }); function getPreferredTheme() { if (!('localStorage' in window)) return 'light'; var storedTheme = localStorage.getItem('theme'); if (storedTheme) { return storedTheme; } return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; } /** * Theme toggler */ (function() { if (!('localStorage' in window)) return; var storedTheme = localStorage.getItem('theme'); function setTheme(theme) { if (theme === 'auto' && window.matchMedia('(prefers-color-scheme: dark)').matches) { document.documentElement.setAttribute('data-bs-theme', 'dark'); update_monaco_theme('dark'); } else { document.documentElement.setAttribute('data-bs-theme', theme); update_monaco_theme(theme == 'auto' ? 'light' : theme); } } setTheme(getPreferredTheme()); function showActiveTheme(theme) { var currentIcon = $('.theme-icon-active').attr('data-icon'); var newIcon = $('[data-bs-theme-value="' + theme +'"] .theme-icon').attr('data-icon'); $('[data-bs-theme-value]').removeClass('active'); $('[data-bs-theme-value="' + theme +'"]').addClass('active'); $('.theme-icon-active') .removeClass(currentIcon) .addClass(newIcon) .attr('data-icon', newIcon); } window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', function() { if (storedTheme !== 'light' || storedTheme !== 'dark') { setTheme(getPreferredTheme()); } }); $(document).ready(function() { showActiveTheme(getPreferredTheme()); $('[data-bs-theme-value]').click(function() { var theme = $(this).data('bs-theme-value'); localStorage.setItem('theme', theme); setTheme(theme); showActiveTheme(theme); }); }); })();