src/methods/ReportTemplateGenerator.tsx
import { ReactElement } from "react"
import ReactDOM from "react-dom"
class ReportTemplateGenerator {
constructor(
rawFilename: string,
rawTemplate: string,
public readonly reportInfo: Pick<
ReportInfo,
"courseName" | "deadline" | "description" | "reportTitle" | "studentName"
> = new ReportInfo(document)
) {
this.filename = this.parseFilename(
rawFilename || ReportTemplateGenerator.defaultFilename
)
this.template = ReportTemplateGenerator.addSignature(
this.parseTemplate(rawTemplate || ReportTemplateGenerator.defaultTemplate)
)
}
public readonly filename: string
public readonly template: string
private GenerateButton = ({
template,
filename,
}: {
template: string
filename: string
}) => (
<button
type="button"
onClick={() => this.downloadReportTemplate(template, filename)}
className="manaba-original-button"
>
{chrome.i18n.getMessage("generate_report_template")}
</button>
)
private downloadReportTemplate = (template: string, filename: string) => {
const blob = new Blob([template], {
type: "application/x-tex",
})
const url = URL.createObjectURL(blob)
chrome.runtime.sendMessage({
url,
filename,
})
}
public appendReportGeneratorRow = () => {
const tbodyQueryString = ".stdlist-reportV2 tbody"
const tbody = document.querySelector<HTMLElement>(tbodyQueryString)
if (!tbody) return
const containerRow = document.createElement("tr")
tbody.appendChild(containerRow)
ReactDOM.render(
<RowContent
header={chrome.i18n.getMessage("report_template")}
data={
<this.GenerateButton
template={this.template}
filename={this.filename}
/>
}
/>,
containerRow
)
}
private injectReportInfoIntoRawText = (
rawText: string,
reportInfo: Pick<
ReportInfo,
"courseName" | "deadline" | "description" | "reportTitle" | "studentName"
>
) =>
Object.entries(reportInfo).reduce(
(acc, [key, value]) =>
acc.replaceAll(
`{{${this.camelcaseToKebabcase(key)}}}`,
typeof value === "string" ? value : value.toLocaleString()
),
rawText
)
private parseFilename = (rawFilename: string) =>
this.injectReportInfoIntoRawText(rawFilename, this.reportInfo)
.replaceAll(/[\\/:*?"<>|]/g, "_")
.trim()
private parseTemplate = (rawTemplate: string) =>
this.injectReportInfoIntoRawText(rawTemplate, this.reportInfo)
private camelcaseToKebabcase = (camelcase: string) =>
camelcase.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, "$1-$2").toLowerCase()
static addSignature = (template: string) =>
`% This file is generated by manaba Enhanced.
${template}`
static defaultFilename = `{{student-name}}_{{course-name}}_{{report-title}}.tex`
static defaultTemplate = `% The deadline is {{deadline}}.
\\documentclass{ltjsarticle}
\\usepackage{listings}
\\begin{document}
\\title{{{course-name}}\\\\{{report-title}}}
\\author{{{student-name}}\\\\${chrome.i18n.getMessage(
"report_template_student_code"
)}}
\\lstset{
numbers=left,
frame=single,
breaklines=true,
}
\\maketitle
% {{description}}
% \\section{${chrome.i18n.getMessage(
"report_template_introduction_section_title"
)}}
\\end{document}
`
}
class ReportInfo {
constructor(private document: Document) {
this.courseName =
this.courseNameElement?.title ?? chrome.i18n.getMessage("course_name")
this.reportTitle =
this.reportTitleElement?.innerText ??
chrome.i18n.getMessage("report_title")
this.description =
this.descriptionElement?.innerText ??
chrome.i18n.getMessage("description")
this.studentName =
this.screennameElement?.innerText ??
chrome.i18n.getMessage("student_name")
this.deadline = new Date(
this.deadlineElement?.innerText.substring(0, 16) ??
chrome.i18n.getMessage("deadline")
)
}
public courseName
public reportTitle
public studentName
public deadline
public description
private courseNameElement = this.document.getElementById("coursename")
private screennameElement = this.document.getElementById("screenname")
private reportTitleElement =
this.document.querySelector<HTMLTableCellElement>(
".stdlist-reportV2 .title th"
)
private tdElements = this.document.querySelectorAll<HTMLTableCellElement>(
".stdlist-reportV2 td"
)
private descriptionElement = this.tdElements[0]
private deadlineElement = this.tdElements[2]
}
const RowContent = ({
header,
data,
}: {
header: string
data: ReactElement | string
}) => (
<>
<th>{header}</th>
<td className="left">{data}</td>
</>
)
export { ReportTemplateGenerator, RowContent }