145 lines
3.5 KiB
TypeScript
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
|
|
} |