ElectronicBabylonianLiterature/ebl-frontend

View on GitHub
src/fragmentarium/ui/fragment/WordExport.tsx

Summary

Maintainability
C
1 day
Test Coverage
A
100%
import React from 'react'
import Promise from 'bluebird'
import { Fragment } from 'fragmentarium/domain/fragment'
import Record from 'fragmentarium/ui/info/Record'
import {
  Document,
  HeadingLevel,
  Paragraph,
  TextRun,
  TableCell,
  TableRow,
  Table,
  WidthType,
  HyperlinkType,
  FootnoteReferenceRun,
} from 'docx'
import {
  generateWordDocument,
  getCreditForHead,
  getFootNotes,
  getGlossary,
  getTransliterationText,
  getFormatedTableCell,
  getTextRun,
  HtmlToWordParagraph,
} from 'common/HtmlToWord'
import {
  getHeading,
  getHyperLinkParagraph,
  isNoteCell,
} from 'common/HtmlToWordUtils'
import { getLineTypeByHtml } from 'common/HtmlLineType'
import { fixHtmlParseOrder } from 'common/HtmlParsing'
import { ReactElement } from 'react'
import TransliterationLines from 'transliteration/ui/TransliterationLines'
import TransliterationNotes from 'transliteration/ui/TransliterationNotes'
import { Glossary } from 'transliteration/ui/Glossary'
import { renderToString } from 'react-dom/server'
import $ from 'jquery'
import WordService from 'dictionary/application/WordService'
import GlossaryFactory from 'transliteration/application/GlossaryFactory'
import { MemoryRouter } from 'react-router-dom'
import { DictionaryContext } from 'dictionary/ui/dictionary-context'
import Markup from 'transliteration/ui/markup'
import { createParagraphs } from 'markup/ui/markup'

export async function wordExport(
  fragment: Fragment,
  wordService: WordService,
  jQueryRef: JQuery
): Promise<Document> {
  const tableHtml: JQuery = $(
    renderToString(
      <MemoryRouter>
        <DictionaryContext.Provider value={wordService}>
          <TransliterationLines text={fragment.text} />
        </DictionaryContext.Provider>
      </MemoryRouter>
    )
  )
  const notesHtml: JQuery = $(
    renderToString(
      <DictionaryContext.Provider value={wordService}>
        <TransliterationNotes notes={fragment.text.notes} />
      </DictionaryContext.Provider>
    )
  )
  const records: JQuery = $(
    renderToString(Record({ record: fragment.uniqueRecord }))
  )
  const footNotes: Paragraph[] = getFootNotes(notesHtml, jQueryRef)
  const tableWithFootnotes = getMainTableWithFootnotes(
    tableHtml,
    footNotes,
    jQueryRef
  )
  return generateWordDocument(
    tableWithFootnotes.footNotes,
    [
      getHeading(fragment.number, true),
      getHyperLinkParagraph(),
      getCreditForHead(records),
      ...getIntroduction(fragment),
      ...tableWithFootnotes.table,
      ...(await getGlossaryOrEmpty(fragment, wordService, jQueryRef)),
    ],
    getHyperLink(fragment)
  )
}

function wrapWithMemoryRouter(component: JSX.Element): ReactElement {
  return <MemoryRouter>{component}</MemoryRouter>
}

function getMainTableWithFootnotes(
  table: JQuery,
  footNotesLines: Paragraph[],
  jQueryRef: JQuery
): { table: Array<Table | Paragraph>; footNotes: Paragraph[] } {
  table.hide()

  jQueryRef.append(table)

  const tablelines: JQuery = table.find('tr')
  fixHtmlParseOrder(tablelines)

  let footNotesCounter = 1

  const rows: TableRow[] = []
  const footNotes: Paragraph[] = []

  tablelines.each((i, el) => {
    const lineType = getLineTypeByHtml($(el))
    const nextElement = $(el).next()
    const nextLineType = getLineTypeByHtml(nextElement)
    if (lineType === 'emptyLine') return
    const tds: TableCell[] = []
    $(el)
      .find('td')
      .each((i, el) => {
        const runs: TextRun[] = []

        if (isNoteCell($(el))) {
          runs.push(new FootnoteReferenceRun(footNotesCounter))
          footNotes.push(footNotesLines[footNotesCounter - 1])
          footNotesCounter++
        } else if (lineType === 'textLine') {
          $(el)
            .find('span,em,sup')
            .each((i, el) => {
              if (
                $(el).contents().text().length > 0 &&
                $(el).contents()[0].nodeType === 3
              ) {
                getTransliterationText($(el), runs)
              }
            })
        } else if (lineType !== 'rulingDollarLine') {
          runs.push(getTextRun($(el)))
        }

        const para: Paragraph[] = [
          new Paragraph({
            children: runs,
            style: 'wellSpaced',
            heading: HeadingLevel.HEADING_1,
          }),
        ]

        const colspan: string | undefined = $(el).is('[colspan]')
          ? $(el).attr('colspan')
          : '1'
        const colspanInt: number = colspan ? parseInt(colspan) : 1

        tds.push(
          getFormatedTableCell(para, nextLineType, nextElement, colspanInt)
        )
      }) //td
    rows.push(new TableRow({ children: tds }))
  }) //tr

  table.remove()
  const wordTable: Array<Table | Paragraph> =
    rows.length > 0
      ? [
          getHeading('Edition'),
          new Table({
            rows: rows,
            width: { size: 100, type: WidthType.PERCENTAGE },
          }),
        ]
      : []
  return { table: wordTable, footNotes: footNotes }
}

function getHyperLink(fragment: Fragment) {
  return {
    headLink: {
      link: 'https://www.ebl.lmu.de/fragmentarium/' + fragment.number,
      text: 'https://www.ebl.lmu.de/fragmentarium/' + fragment.number,
      type: HyperlinkType.EXTERNAL,
    },
  }
}

function getIntroduction(fragment: Fragment): Paragraph[] {
  const paragraphs = createParagraphs(fragment.introduction.parts).map(
    (paragraphParts, index) => {
      return HtmlToWordParagraph(
        $(
          renderToString(
            wrapWithMemoryRouter(
              <Markup parts={paragraphParts} key={index} container={'p'} />
            )
          )
        )
      )
    }
  )
  return [getHeading('Introduction'), ...paragraphs]
}

async function getGlossaryOrEmpty(
  fragment: Fragment,
  wordService: WordService,
  jQueryRef: JQuery
): Promise<Paragraph[]> {
  const glossaryFactory: GlossaryFactory = new GlossaryFactory(wordService)
  const glossaryJsx: JSX.Element = await glossaryFactory
    .createGlossary(fragment.text)
    .then((glossaryData) => {
      return Glossary({ data: glossaryData })
    })
  const glossaryHtml: JQuery = $(
    renderToString(
      wrapWithMemoryRouter(
        <DictionaryContext.Provider value={wordService}>
          {glossaryJsx}
        </DictionaryContext.Provider>
      )
    )
  )
  return glossaryHtml.children().length > 1
    ? getGlossary(glossaryHtml, jQueryRef)
    : []
}