From c0238a1f09509c4ca90a77224341f61101eb926b Mon Sep 17 00:00:00 2001 From: drafear Date: Tue, 18 Dec 2018 17:46:13 +0900 Subject: [PATCH 1/3] chore: add a setting for add-tweet-button --- package.json | 1 + src/options-page/options.ts | 178 ++++++++++++++++++++++++++++++++++++ 2 files changed, 179 insertions(+) create mode 100644 src/options-page/options.ts diff --git a/package.json b/package.json index 1571a13..3938e1d 100644 --- a/package.json +++ b/package.json @@ -101,6 +101,7 @@ } ], "typescript/explicit-function-return-type": 0, + "no-useless-constructor": 0, "no-alert": 0, "no-negated-condition": 0, "capitalized-comments": 0, diff --git a/src/options-page/options.ts b/src/options-page/options.ts new file mode 100644 index 0000000..014f9e9 --- /dev/null +++ b/src/options-page/options.ts @@ -0,0 +1,178 @@ +class Language { + constructor(public readonly id: string, public readonly name: string) {} +} + +class Switch { + public readonly defaultValue: boolean; + + constructor( + public readonly onText: string, + public readonly offText: string, + public readonly storageKey: string, + defaultValue = true, + ) { + this.defaultValue = defaultValue; + } + + readValue(): Promise { + return new Promise(resolve => + chrome.storage.sync.get([this.storageKey], result => { + if (this.storageKey in result) { + resolve(Boolean(result[this.storageKey])); + } else { + chrome.storage.sync.set({ [this.storageKey]: this.defaultValue }); + resolve(this.defaultValue); + } + }), + ); + } + + cbChandeListener(e: Event) { + const value = (e.currentTarget as HTMLInputElement).checked; + const data: { [key: string]: boolean } = {}; + data[this.storageKey] = value; + chrome.storage.sync.set(data); + } + + async toElem() { + const value: boolean = await this.readValue(); + const label = document.createElement('label'); + label.classList.add('switch'); + const cb = document.createElement('input'); + cb.type = 'checkbox'; + cb.checked = value; + cb.addEventListener('change', this.cbChandeListener.bind(this)); + label.appendChild(cb); + const div = document.createElement('div'); + div.dataset.on = this.onText; + div.dataset.off = this.offText; + label.appendChild(div); + return label; + } +} +class Choice { + constructor(public readonly name: string, public readonly component: Switch) {} + + async toElem() { + const li = document.createElement('li'); + const left = document.createElement('div'); + left.textContent = this.name; + const right = document.createElement('div'); + right.appendChild(await this.component.toElem()); + li.appendChild(left); + li.appendChild(right); + return li; + } +} + +class Group { + constructor(public readonly name: string, public readonly choices: Choice[]) {} + + async toElem() { + const div = document.createElement('div'); + const header = document.createElement('h2'); + header.textContent = this.name; + div.appendChild(header); + const ul = document.createElement('ul'); + ul.classList.add('settings-list'); + for (const option of this.choices) { + ul.appendChild(await option.toElem()); + } + div.appendChild(ul); + return div; + } +} + +const makeWarnChoices = (langs: Language[]): Choice[] => { + // Bash, Text + const defaults = new Set(['3001', '3027']); + const choices: Choice[] = []; + for (const lang of langs) { + const defaultValue = Boolean(defaults.has(lang.id)); + choices.push(new Choice(lang.name, new Switch('on', 'off', `warn-${lang.id}`, defaultValue))); + } + return choices; +}; + +const languages = [ + new Language('3003', 'C++14 (GCC)'), + new Language('3001', 'Bash'), + new Language('3002', 'C (GCC)'), + new Language('3004', 'C (Clang)'), + new Language('3005', 'C++14 (Clang)'), + new Language('3006', 'C#'), + new Language('3007', 'Clojure'), + new Language('3008', 'Common Lisp'), + new Language('3009', 'D (DMD64)'), + new Language('3010', 'D (LDC)'), + new Language('3011', 'D (GDC)'), + new Language('3012', 'Fortran'), + new Language('3013', 'Go'), + new Language('3014', 'Haskell'), + new Language('3015', 'Java7'), + new Language('3016', 'Java8'), + new Language('3017', 'JavaScript'), + new Language('3018', 'OCaml'), + new Language('3019', 'Pascal'), + new Language('3020', 'Perl'), + new Language('3021', 'PHP'), + new Language('3022', 'Python2'), + new Language('3023', 'Python3'), + new Language('3024', 'Ruby'), + new Language('3025', 'Scala'), + new Language('3026', 'Scheme'), + new Language('3027', 'Text'), + new Language('3028', 'Visual Basic'), + new Language('3029', 'C++ (GCC)'), + new Language('3030', 'C++ (Clang)'), + new Language('3501', 'Objective-C (GCC)'), + new Language('3502', 'Objective-C (Clang)'), + new Language('3503', 'Swift'), + new Language('3504', 'Rust'), + new Language('3505', 'Sed'), + new Language('3506', 'Awk'), + new Language('3507', 'Brainfuck'), + new Language('3508', 'Standard ML'), + new Language('3509', 'PyPy2'), + new Language('3510', 'PyPy3'), + new Language('3511', 'Crystal'), + new Language('3512', 'F#'), + new Language('3513', 'Unlambda'), + new Language('3514', 'Lua'), + new Language('3515', 'LuaJIT'), + new Language('3516', 'MoonScript'), + new Language('3517', 'Ceylon'), + new Language('3518', 'Julia'), + new Language('3519', 'Octave'), + new Language('3520', 'Nim'), + new Language('3521', 'TypeScript'), + new Language('3522', 'Perl6'), + new Language('3523', 'Kotlin'), + new Language('3524', 'PHP7'), + new Language('3525', 'COBOL - Fixed'), + new Language('3526', 'COBOL - Free'), +]; + +const groups = [ + new Group('Non-beta', [new Choice('Beta Tab', new Switch('enable', 'disable', 'beta-tab'))]), + new Group('Notification', [ + new Choice('Judge Result', new Switch('on', 'off', 'notify-judge-result')), + new Choice('Clarification', new Switch('on', 'off', 'notify-clarification')), + ]), + new Group('Dropdown', [ + new Choice('Hover', new Switch('hover', 'click', 'dropdown-hover')), + new Choice('Problem Tab', new Switch('enable', 'disable', 'dropdown-problem')), + ]), + new Group('Tweet Button', [new Choice('Enable', new Switch('enable', 'disable', 'add-tweet-button'))]), + new Group('Warning on Submission', [ + new Choice('Enable', new Switch('enable', 'disable', 'submission-warning')), + ...makeWarnChoices(languages), + ]), +]; + +document.addEventListener('DOMContentLoaded', async () => { + const root = document.documentElement; + for (const group of groups) { + root.appendChild(await group.toElem()); + } +}); From 165a12f91280fef8747f8e002e2c4faa449d4365 Mon Sep 17 00:00:00 2001 From: drafear Date: Thu, 20 Dec 2018 02:16:53 +0900 Subject: [PATCH 2/3] feat: Add a tweet button in the page of your Competition History --- .vscode/settings.json | 16 ++- package.json | 4 +- src/content/add-tweet-button.ts | 80 ++++++++++++ src/content/betalib.ts | 68 ++++++++++ src/css/add-twitter-button.css | 9 ++ src/css/add-twitter-button.less | 25 ++++ src/manifest.json | 77 ++++++++--- src/options-page/options.js | 219 -------------------------------- src/options-page/options.ts | 10 +- 9 files changed, 266 insertions(+), 242 deletions(-) create mode 100644 src/content/add-tweet-button.ts create mode 100644 src/css/add-twitter-button.css create mode 100644 src/css/add-twitter-button.less delete mode 100644 src/options-page/options.js diff --git a/.vscode/settings.json b/.vscode/settings.json index 1474f5d..020e13a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -25,5 +25,19 @@ "workbench.statusBar.feedback.visible": false, "xo.format.enable": true, "xo.enable": true, - "gitlens.views.repositories.files.layout": "list" + "gitlens.views.repositories.files.layout": "list", + "cSpell.words": [ + "atcoder", + "betalib", + "brainfuck", + "caml", + "clar", + "commonlib", + "drafear", + "dropdown", + "glyphicon", + "o", + "oninstall", + "unlambda" + ] } diff --git a/package.json b/package.json index 3938e1d..123a633 100644 --- a/package.json +++ b/package.json @@ -3,13 +3,13 @@ "description": "Comfort your atcoder life. For more detail, visit https://github.com/drafear/comfortable-atcoder", "scripts": { "build": "run-s build:*", - "build:xo": "xo src/**/*.ts", "build:init": "rm -rf dist", "build:cp": "cp -r src dist && rm -rf dist/**/*.less", + "build:css": "less-watch-compiler --run-once src dist", "build:ts": "parcel build --target node src/**/*.ts", "start": "npm run watch", "watch": "run-p watch:*", - "watch:less": "less-watch-compiler src src", + "watch:less": "less-watch-compiler src dist", "watch:build": "parcel --target node src/**/*.ts", "watch:test": "jest --watch", "package": "npm run build && zip -rq Release.zip dist" diff --git a/src/content/add-tweet-button.ts b/src/content/add-tweet-button.ts new file mode 100644 index 0000000..a852576 --- /dev/null +++ b/src/content/add-tweet-button.ts @@ -0,0 +1,80 @@ +import * as Commonlib from './all'; +import * as Betalib from './betalib'; + +Commonlib.runIfEnableAndLoad('add-tweet-button', async () => { + const isMyPage = $('#user-nav-tabs .glyphicon-cog').length >= 1; + if (!isMyPage) { + return; + } + + const isDetailPage = /^\/users\/[^/]+\/history\/?$/.test(location.pathname); + const userId = (location.pathname.match(/^\/users\/([^/]+)/) as string[])[1]; + + async function getTable(): Promise> { + // if (isDetailPage) { + // return $('#history'); + // } + // else { + const html = await (await fetch(`/users/${userId}/history`)).text(); + return $(html).find('#history'); + // } + } + + function getLatestContestResult(contestResults: Betalib.ContestResult[]): Betalib.ContestResult | null { + if (contestResults.length === 0) { + return null; + } + let res = contestResults[0]; + for (const result of contestResults) { + if (result.date > res.date) { + res = result; + } + } + return res; + } + + function makeTweetText(contestResult: Betalib.ContestResult, isHighest = false): string { + const r = contestResult; + if (r instanceof Betalib.RatedContestResult) { + const highestStr = isHighest ? ', Highest!!' : ''; + return `I took ${r.getRankStr()} place in ${r.contestName}\n\nRating: ${r.newRating - r.diff} -> ${r.newRating} (${r.getDiffStr()}${highestStr})\nPerformance: ${r.performance}\n#${r.contestId}`; + } + else { + return `I took ${r.getRankStr()} place in ${r.contestName}\n#${r.contestId}`; + } + } + + function isHighest(targetContestResult: Betalib.ContestResult, contestResults: Betalib.ContestResult[]) { + if (!(targetContestResult instanceof Betalib.RatedContestResult)) { + return false; + } + for (const result of contestResults) { + if (result.contestId === targetContestResult.contestId) { + continue; + } + if (!(result instanceof Betalib.RatedContestResult)) { + continue; + } + if (result.newRating >= targetContestResult.newRating) { + return false; + } + } + return true; + } + + const $table = await getTable(); + const contestResults = Betalib.GetContestResultsFromTable($table); + const latestContestResult = getLatestContestResult(contestResults); + // 一度も参加したことがない + if (latestContestResult === null) { + return; + } + const tweetContent = makeTweetText(latestContestResult, isHighest(latestContestResult, contestResults)); + const text = navigator.language === 'ja' ? '最新のコンテスト結果をツイート' : 'Tweet the result of the latest contest'; + const $tweetButton = $('').addClass('tweet').text(text) + .prop('href', `https://twitter.com/share?url=''&text=${encodeURIComponent(tweetContent)}`) + .prop('target', '_blank'); + if (isDetailPage) { + $('#history_wrapper > div.row:first-child > .col-sm-6:first-child').eq(0).prepend($tweetButton); + } +}); diff --git a/src/content/betalib.ts b/src/content/betalib.ts index dcf69cc..d9160fd 100644 --- a/src/content/betalib.ts +++ b/src/content/betalib.ts @@ -122,6 +122,45 @@ export class JudgeStatus { } } +export abstract class ContestResult { + constructor(public readonly date: Date, public readonly contestName: string, public readonly contestId: string, public readonly rank: number, public readonly diff: number) { } + abstract isRated(): boolean; + getRankStr(): string { + switch (this.rank % 10) { + case 1: + return `${this.rank}st`; + case 2: + return `${this.rank}nd`; + case 3: + return `${this.rank}rd`; + default: + return `${this.rank}th`; + } + } + getDiffStr(): string { + if (this.diff > 0) { + return `+${this.diff}`; + } + if (this.diff < 0) { + return this.diff.toString(); + } + return '±0'; + } +} +export class UnRatedContestResult extends ContestResult { + isRated() { + return false; + } +} +export class RatedContestResult extends ContestResult { + constructor(date: Date, contestName: string, contestId: string, rank: number, diff: number, public readonly performance: number, public readonly newRating: number) { + super(date, contestName, contestId, rank, diff); + } + isRated() { + return true; + } +} + export function parseJudgeStatus(text: string): JudgeStatus { const reg = /[ \s]/g; // WJ @@ -230,3 +269,32 @@ export async function getProblems(): Promise { }); return res; } + +export function GetContestResultsFromTable($table: JQuery): ContestResult[] { + const res: ContestResult[] = []; + const $th = $('thead > tr > th', $table); + const indexes = getIndexes($th, { + date: ['Date', '日付'], + contest: ['Contest', 'コンテスト'], + rank: ['Rank', '順位'], + performance: ['Performance', 'パフォーマンス'], + newRating: ['NewRating', '新Rating'], + diff: ['Diff', '差分'], + }); + $('tbody > tr', $table).each((idx, tr) => { + const $tds = $(tr).children('td'); + const date = new Date($tds.eq(indexes.date).text()); + const $contest = $tds.eq(indexes.contest).children('a').eq(0); + const contestName = $contest.text(); + const contestId = (($contest.prop('href') as string).match(/\/contests\/([^\/]+)\/?$/) as string[])[1]; + const rank = Number($tds.eq(indexes.rank).text()); + const performanceStr = $tds.eq(indexes.performance).text(); + const newRatingStr = $tds.eq(indexes.newRating).text(); + const diff = Number($tds.eq(indexes.diff).text().replace(/[^0-9]/g, '')); + const isRated = performanceStr !== '-'; + res[idx] = + isRated ? new RatedContestResult(date, contestName, contestId, rank, diff, Number(performanceStr), Number(newRatingStr)) + : new UnRatedContestResult(date, contestName, contestId, rank, diff); + }); + return res; +} diff --git a/src/css/add-twitter-button.css b/src/css/add-twitter-button.css new file mode 100644 index 0000000..4171fb1 --- /dev/null +++ b/src/css/add-twitter-button.css @@ -0,0 +1,9 @@ +.row { + display: flex; + justify-content: space-between; +} +.tweet { + padding: 3px; + background-color: hsl(220, 70%, 60%); + color: white; +} diff --git a/src/css/add-twitter-button.less b/src/css/add-twitter-button.less new file mode 100644 index 0000000..3bf1851 --- /dev/null +++ b/src/css/add-twitter-button.less @@ -0,0 +1,25 @@ +#history_wrapper > .row { + display: flex; + justify-content: space-between; + align-items: center; +} + +.tweet { + display: inline-block; + padding: 5px; + background-color: hsl(220, 70%, 60%); + border-radius: 3px; + color: white; + text-decoration: none; + transition: all .2s; + &:hover, + &:active, + &:focus, + &:visited { + color: white; + text-decoration: none; + } + &:hover { + background-color: hsl(220, 70%, 50%); + } +} diff --git a/src/manifest.json b/src/manifest.json index ec0407c..1be03c7 100755 --- a/src/manifest.json +++ b/src/manifest.json @@ -2,34 +2,77 @@ "name": "Comfortable Atcoder", "version": "1.5.3", "manifest_version": 2, - "description": "Comfort your atcoder life. For more detail, visit https://github.com/drafear/comfortable-atcoder", - "author": "drafear", + "description": "Comfort your atcoder life. For more detail, visit https://github.com/drafear/comfortable-atcoder", + "author": "drafear", "content_scripts": [ { - "matches": ["*://atcoder.jp/contests/*", "*://*.contest.atcoder.jp/*"], - "exclude_matches": ["*://*.contest.atcoder.jp/users/*"], - "js": ["lib/jquery.min.js", "lib/jquery.cookie.js", "content/all.js"], - "css": ["css/all.css"], + "matches": [ + "*://atcoder.jp/contests/*", + "*://atcoder.jp/users/*", + "*://*.contest.atcoder.jp/*" + ], + "exclude_matches": [ + "*://*.contest.atcoder.jp/users/*" + ], + "js": [ + "lib/jquery.min.js", + "lib/jquery.cookie.js", + "content/all.js" + ], + "css": [ + "css/all.css" + ], "run_at": "document_start" }, { - "matches": ["*://atcoder.jp/contests/*"], - "js": ["content/betalib.js", "content/dropdown-modify.js", "content/clar-notify.js"], - "css": ["css/dropdown-modify.css"], + "matches": [ + "*://atcoder.jp/contests/*" + ], + "js": [ + "content/betalib.js", + "content/dropdown-modify.js", + "content/clar-notify.js" + ], + "css": [ + "css/dropdown-modify.css" + ], "run_at": "document_start" }, { - "matches": ["*://atcoder.jp/contests/*/submissions/me"], - "js": ["content/result-notify.js"], + "matches": [ + "*://atcoder.jp/contests/*/submissions/me" + ], + "js": [ + "content/result-notify.js" + ], "run_at": "document_start" }, { - "matches": ["*://atcoder.jp/contests/*/submit*", "*://atcoder.jp/contests/*/tasks/*"], - "js": ["content/submission-warning.js"], + "matches": [ + "*://atcoder.jp/contests/*/submit*", + "*://atcoder.jp/contests/*/tasks/*" + ], + "js": [ + "content/submission-warning.js" + ], + "run_at": "document_start" + }, + { + "matches": [ + "*://atcoder.jp/users/*/history*" + ], + "js": [ + "content/add-tweet-button.js" + ], + "css": [ + "css/add-twitter-button.css" + ], "run_at": "document_start" } ], - "web_accessible_resources": ["image/beta.png"], + "web_accessible_resources": [ + "image/beta.png" + ], "background": { "scripts": [ "lib/jquery.min.js", @@ -42,7 +85,11 @@ "persistent": false }, "options_page": "options-page/options.html", - "permissions": ["notifications", "storage", ""], + "permissions": [ + "notifications", + "storage", + "" + ], "icons": { "16": "image/icon.png", "48": "image/icon.png", diff --git a/src/options-page/options.js b/src/options-page/options.js deleted file mode 100644 index 337e8ee..0000000 --- a/src/options-page/options.js +++ /dev/null @@ -1,219 +0,0 @@ -class Language { - constructor(id, name) { - this.id = id; - this.name = name; - } -} - -class Switch { - constructor(onText, offText, storageKey, defaultValue = true) { - this.onText = onText; - this.offText = offText; - this.storageKey = storageKey; - this.defaultValue = defaultValue; - } - - readValue() { - return new Promise( - resolve => chrome.storage.sync.get( - [this.storageKey], - result => { - if (this.storageKey in result) { - resolve(Boolean(result[this.storageKey])); - } else { - chrome.storage.sync.set({[this.storageKey]: this.defaultValue}); - resolve(this.defaultValue); - } - } - ) - ); - } - - cbChandeListener(e) { - const value = e.currentTarget.checked; - const data = {}; - data[this.storageKey] = value; - chrome.storage.sync.set(data); - } - - async toElem() { - const value = await this.readValue(); - const label = document.createElement('label'); - label.classList.add('switch'); - const cb = document.createElement('input'); - cb.type = 'checkbox'; - cb.checked = value; - cb.addEventListener('change', this.cbChandeListener.bind(this)); - label.appendChild(cb); - const div = document.createElement('div'); - div.dataset.on = this.onText; - div.dataset.off = this.offText; - label.appendChild(div); - return label; - } -} -class Option { - constructor(name, component) { - this.name = name; - this.component = component; - } - - async toElem() { - const li = document.createElement('li'); - const left = document.createElement('div'); - left.textContent = this.name; - const right = document.createElement('div'); - right.appendChild(await this.component.toElem()); - li.appendChild(left); - li.appendChild(right); - return li; - } -} - -class Group { - constructor(name, options) { - this.name = name; - this.options = options; - } - - async toElem() { - const div = document.createElement('div'); - const header = document.createElement('h2'); - header.textContent = this.name; - div.appendChild(header); - const ul = document.createElement('ul'); - ul.classList.add('settings-list'); - for (const option of this.options) { - ul.appendChild(await option.toElem()); - } - div.appendChild(ul); - return div; - } -} - -function makeWarnOptions(langs) { - // Bash, Text - const defaults = new Set(['3001', '3027']); - const options = []; - for (const lang of langs) { - const defaultValue = Boolean(defaults.has(lang.id)); - options.push( - new Option( - lang.name, - new Switch('on', 'off', `warn-${lang.id}`, defaultValue) - ) - ); - } - return options; -} - -const languages = [ - new Language('3003', 'C++14 (GCC)'), - new Language('3001', 'Bash'), - new Language('3002', 'C (GCC)'), - new Language('3004', 'C (Clang)'), - new Language('3005', 'C++14 (Clang)'), - new Language('3006', 'C#'), - new Language('3007', 'Clojure'), - new Language('3008', 'Common Lisp'), - new Language('3009', 'D (DMD64)'), - new Language('3010', 'D (LDC)'), - new Language('3011', 'D (GDC)'), - new Language('3012', 'Fortran'), - new Language('3013', 'Go'), - new Language('3014', 'Haskell'), - new Language('3015', 'Java7'), - new Language('3016', 'Java8'), - new Language('3017', 'JavaScript'), - new Language('3018', 'OCaml'), - new Language('3019', 'Pascal'), - new Language('3020', 'Perl'), - new Language('3021', 'PHP'), - new Language('3022', 'Python2'), - new Language('3023', 'Python3'), - new Language('3024', 'Ruby'), - new Language('3025', 'Scala'), - new Language('3026', 'Scheme'), - new Language('3027', 'Text'), - new Language('3028', 'Visual Basic'), - new Language('3029', 'C++ (GCC)'), - new Language('3030', 'C++ (Clang)'), - new Language('3501', 'Objective-C (GCC)'), - new Language('3502', 'Objective-C (Clang)'), - new Language('3503', 'Swift'), - new Language('3504', 'Rust'), - new Language('3505', 'Sed'), - new Language('3506', 'Awk'), - new Language('3507', 'Brainfuck'), - new Language('3508', 'Standard ML'), - new Language('3509', 'PyPy2'), - new Language('3510', 'PyPy3'), - new Language('3511', 'Crystal'), - new Language('3512', 'F#'), - new Language('3513', 'Unlambda'), - new Language('3514', 'Lua'), - new Language('3515', 'LuaJIT'), - new Language('3516', 'MoonScript'), - new Language('3517', 'Ceylon'), - new Language('3518', 'Julia'), - new Language('3519', 'Octave'), - new Language('3520', 'Nim'), - new Language('3521', 'TypeScript'), - new Language('3522', 'Perl6'), - new Language('3523', 'Kotlin'), - new Language('3524', 'PHP7'), - new Language('3525', 'COBOL - Fixed'), - new Language('3526', 'COBOL - Free'), -]; - -const groups = [ - new Group( - 'Non-beta', - [ - new Option( - 'Beta Tab', - new Switch('enable', 'disable', 'beta-tab'), - ), - ], - ), - new Group( - 'Notification', - [ - new Option( - 'Judge Result', - new Switch('on', 'off', 'notify-judge-result'), - ), - new Option( - 'Clarification', - new Switch('on', 'off', 'notify-clarification'), - ), - ], - ), - new Group( - 'Dropdown', - [ - new Option( - 'Hover', - new Switch('hover', 'click', 'dropdown-hover'), - ), - new Option( - 'Problem Tab', - new Switch('enable', 'disable', 'dropdown-problem'), - ), - ], - ), - new Group( - 'Warning on Submission', - [ - new Option('Enable', new Switch('enable', 'disable', 'submission-warning')), - ...makeWarnOptions(languages), - ], - ), -]; - -document.addEventListener('DOMContentLoaded', async () => { - const root = document.documentElement; - for (const group of groups) { - root.appendChild(await group.toElem()); - } -}); diff --git a/src/options-page/options.ts b/src/options-page/options.ts index 014f9e9..a5f0f09 100644 --- a/src/options-page/options.ts +++ b/src/options-page/options.ts @@ -1,5 +1,5 @@ class Language { - constructor(public readonly id: string, public readonly name: string) {} + constructor(public readonly id: string, public readonly name: string) { } } class Switch { @@ -27,7 +27,7 @@ class Switch { ); } - cbChandeListener(e: Event) { + cbChangeListener(e: Event) { const value = (e.currentTarget as HTMLInputElement).checked; const data: { [key: string]: boolean } = {}; data[this.storageKey] = value; @@ -41,7 +41,7 @@ class Switch { const cb = document.createElement('input'); cb.type = 'checkbox'; cb.checked = value; - cb.addEventListener('change', this.cbChandeListener.bind(this)); + cb.addEventListener('change', this.cbChangeListener.bind(this)); label.appendChild(cb); const div = document.createElement('div'); div.dataset.on = this.onText; @@ -51,7 +51,7 @@ class Switch { } } class Choice { - constructor(public readonly name: string, public readonly component: Switch) {} + constructor(public readonly name: string, public readonly component: Switch) { } async toElem() { const li = document.createElement('li'); @@ -66,7 +66,7 @@ class Choice { } class Group { - constructor(public readonly name: string, public readonly choices: Choice[]) {} + constructor(public readonly name: string, public readonly choices: Choice[]) { } async toElem() { const div = document.createElement('div'); From 519d728140d21f5ba130ab792bd1cf38b77720a9 Mon Sep 17 00:00:00 2001 From: drafear Date: Thu, 20 Dec 2018 18:41:08 +0900 Subject: [PATCH 3/3] fix: beta-button --- src/manifest.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/manifest.json b/src/manifest.json index ec0407c..99bb059 100755 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,6 +1,6 @@ { "name": "Comfortable Atcoder", - "version": "1.5.3", + "version": "1.5.4", "manifest_version": 2, "description": "Comfort your atcoder life. For more detail, visit https://github.com/drafear/comfortable-atcoder", "author": "drafear", @@ -27,6 +27,11 @@ "matches": ["*://atcoder.jp/contests/*/submit*", "*://atcoder.jp/contests/*/tasks/*"], "js": ["content/submission-warning.js"], "run_at": "document_start" + }, + { + "matches": ["*://*.contest.atcoder.jp/*"], + "js": ["content/link-to-beta.js"], + "run_at": "document_start" } ], "web_accessible_resources": ["image/beta.png"],