heartb-web-oa-master/app/utils/chapter.ts

145 lines
3.5 KiB
TypeScript

export interface ChapterItem {
rawTitle:string
title:string
/**
* the chapter content
*/
text:string
/**
* the chapter number
*/
number:number
key:string
indexOf:number
}
export type Chapters = ChapterItem[]
function formatLines(text:string){
const lines = text.split('\n')
// .reduce<string[]>((result,line)=>{
// const childLines = line.split('\r\n').map(childLine=>{
// return childLine.split("\r")
// })
// return result.concat(
// ...childLines
// )
// },[])
return lines
}
export function splitChapter(text:string){
const lines = formatLines(text)
const chapters:Chapters = []
/**
* the chapters array index, not the chapter number
* like pointer
*/
let chapterIndex = -1
let offset = 0
for(let lineIndex=0;lineIndex<lines.length;lineIndex++) {
const lineText = lines[lineIndex]
offset += lineText.length
if(!lineText || !lineText.trim()) {
continue
}
const title = getChapterTitle(lineText)
if(title!==null){
++chapterIndex;
chapters[chapterIndex] = {
rawTitle:lineText,
title,
text:"",
number:chapterIndex+1,
key:generateKey(),
indexOf:Math.max(0,offset-1)
}
// lastLineOffset+=lineText.length
continue
}
if(chapterIndex===-1) {
chapterIndex = 0
chapters[chapterIndex] = {
title:"",
rawTitle:"",
text:lineText+"\n",
number:1,
key:generateKey(),
indexOf:Math.max(0,offset)
}
continue
}
const chapter = chapters[chapterIndex]
chapter.text += lineText
// chapter.indexOf = lastLineOffset+lineText.length
// lastLineOffset = chapter.indexOf
}
console.info("chapters",chapters)
return chapters
}
function getChapterTitle(text:string){
const matchTitleSpecialCharRegexp = /[^\s\da-z]/i
// example: Chapter 1
if(/^chapter\s*\d+/i.test(text)) {
return text.replace(/^chapter\s*\d+/i,"").replace(matchTitleSpecialCharRegexp,"").trim()
}
// example Chapter: title
// example Chapter 2: title
// example Chapter one: title
// example Chapter One: "title"
// example Chapter One: [title]
// example Chapter III: [title]
// example Chapter: [title]
if(/^chapter\s*.*[^\s\da-z]([\s\da-z]+)/i.test(text)) {
return text.replace(/^chapter\s*.*[^\s\da-z]([\s\da-z]+)/i,"$1").replace(matchTitleSpecialCharRegexp,"").trim()
}
if(
/^([-=+<>#\*])\1{2,}$/.test(text)
) {
return ""
}
if(/SPLIT\s*CHAPTER/.test(text)) return ""
if(/CHAPTER\s*SPLIT/.test(text)) return ""
return null
}
function randomString(count:number) {
const seeds = ['0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'];
return Array(count).fill("").map(()=>{
const index = Math.floor(Math.random() * 1000) % (seeds.length)
return seeds[index]
}).join("")
}
const generateKey = () => randomString(20)
export async function readFile(file:File){
const reader = new FileReader()
const promise = new Promise<string>((resolve,reject)=>{
reader.addEventListener("loadend",function(){
let txt = reader.result as unknown as string
txt = txt.replace('\r\n','\n').replace('\r','\n')
resolve(reader.result as unknown as string)
})
reader.addEventListener("error",reject)
})
reader.readAsText(file,"utf-8")
return promise
}