diff options
author | bobzel <zzzman@gmail.com> | 2024-05-14 23:15:24 -0400 |
---|---|---|
committer | bobzel <zzzman@gmail.com> | 2024-05-14 23:15:24 -0400 |
commit | 3534aaf88a3c30a474b3b5a5b7f04adfe6f15fac (patch) | |
tree | 47fb7a8671b209bd4d76e0f755a5b035c6936607 /src/fields/RichTextUtils.ts | |
parent | 87bca251d87b5a95da06b2212400ce9427152193 (diff) | |
parent | 5cb7ad90e120123ca572e8ef5b1aa6ca41581134 (diff) |
Merge branch 'restoringEslint' into sarah-ai-visualization
Diffstat (limited to 'src/fields/RichTextUtils.ts')
-rw-r--r-- | src/fields/RichTextUtils.ts | 212 |
1 files changed, 113 insertions, 99 deletions
diff --git a/src/fields/RichTextUtils.ts b/src/fields/RichTextUtils.ts index b84a91709..3763dcd2c 100644 --- a/src/fields/RichTextUtils.ts +++ b/src/fields/RichTextUtils.ts @@ -1,21 +1,25 @@ +/* eslint-disable no-await-in-loop */ +/* eslint-disable no-use-before-define */ import { AssertionError } from 'assert'; -import { docs_v1 } from 'googleapis'; +import * as Color from 'color'; +import { docs_v1 as docsV1 } from 'googleapis'; import { Fragment, Mark, Node } from 'prosemirror-model'; import { sinkListItem } from 'prosemirror-schema-list'; import { EditorState, TextSelection, Transaction } from 'prosemirror-state'; -import { GoogleApiClientUtils } from '../client/apis/google_docs/GoogleApiClientUtils'; -import { GooglePhotos } from '../client/apis/google_docs/GooglePhotosClientUtils'; +import { ClientUtils, DashColor } from '../ClientUtils'; +import { Utils } from '../Utils'; import { DocServer } from '../client/DocServer'; -import { Docs, DocUtils } from '../client/documents/Documents'; import { Networking } from '../client/Network'; +import { GoogleApiClientUtils } from '../client/apis/google_docs/GoogleApiClientUtils'; +import { GooglePhotos } from '../client/apis/google_docs/GooglePhotosClientUtils'; +import { Docs } from '../client/documents/Documents'; +import { DocUtils } from '../client/documents/DocUtils'; import { FormattedTextBox } from '../client/views/nodes/formattedText/FormattedTextBox'; import { schema } from '../client/views/nodes/formattedText/schema_rts'; -import { DashColor, Utils } from '../Utils'; import { Doc, Opt } from './Doc'; import { Id } from './FieldSymbols'; import { RichTextField } from './RichTextField'; import { Cast, StrCast } from './Types'; -import * as Color from 'color'; export namespace RichTextUtils { const delimiter = '\n'; @@ -47,13 +51,11 @@ export namespace RichTextUtils { return JSON.stringify(state); }; - export const Synthesize = (plainText: string, oldState?: RichTextField) => { - return new RichTextField(ToProsemirrorState(plainText, oldState), plainText); - }; + export const Synthesize = (plainText: string, oldState?: RichTextField) => new RichTextField(ToProsemirrorState(plainText, oldState), plainText); export const ToPlainText = (state: EditorState) => { // Because we're working with plain text, just concatenate all paragraphs - const content = state.doc.content; + const { content } = state.doc; const paragraphs: Node[] = []; content.forEach(node => node.type.name === 'paragraph' && paragraphs.push(node)); @@ -112,9 +114,9 @@ export namespace RichTextUtils { agnostic: string; } - const parseInlineObjects = async (document: docs_v1.Schema$Document): Promise<Map<string, ImageTemplate>> => { + const parseInlineObjects = async (document: docsV1.Schema$Document): Promise<Map<string, ImageTemplate>> => { const inlineObjectMap = new Map<string, ImageTemplate>(); - const inlineObjects = document.inlineObjects; + const { inlineObjects } = document; if (inlineObjects) { const objects = Object.keys(inlineObjects).map(objectId => inlineObjects[objectId]); @@ -140,8 +142,8 @@ export namespace RichTextUtils { inlineObjectMap.set(object.objectId!, { title: embeddedObject.title || `Imported Image from ${document.title}`, width, - url: Utils.prepend(_m.client), - agnostic: Utils.prepend(agnostic.client), + url: ClientUtils.prepend(_m.client), + agnostic: ClientUtils.prepend(agnostic.client), }); } } @@ -162,7 +164,7 @@ export namespace RichTextUtils { const inlineObjectMap = await parseInlineObjects(document); const title = document.title!; const { text, paragraphs } = GoogleApiClientUtils.Docs.Utils.extractText(document); - let state = FormattedTextBox.blankState(); + let state = EditorState.create(new FormattedTextBox({} as any).config); const structured = parseLists(paragraphs); let position = 3; @@ -170,10 +172,12 @@ export namespace RichTextUtils { const indentMap = new Map<ListGroup, BulletPosition[]>(); let globalOffset = 0; const nodes: Node[] = []; + // eslint-disable-next-line no-restricted-syntax for (const element of structured) { if (Array.isArray(element)) { lists.push(element); const positions: BulletPosition[] = []; + // eslint-disable-next-line no-loop-func const items = element.map(paragraph => { const item = listItem(state.schema, paragraph.contents); const sinks = paragraph.bullet!; @@ -187,41 +191,42 @@ export namespace RichTextUtils { }); indentMap.set(element, positions); nodes.push(list(state.schema, items)); + } else if (element.contents.some(child => 'inlineObjectId' in child)) { + const group = element.contents; + // eslint-disable-next-line no-loop-func + group.forEach((child, i) => { + let node: Opt<Node>; + if ('inlineObjectId' in child) { + node = imageNode(state.schema, inlineObjectMap.get(child.inlineObjectId!)!, textNote); + } else if ('content' in child && (i !== group.length - 1 || child.content!.removeTrailingNewlines().length)) { + node = paragraphNode(state.schema, [child]); + } + if (node) { + position += node.nodeSize; + nodes.push(node); + } + }); } else { - if (element.contents.some(child => 'inlineObjectId' in child)) { - const group = element.contents; - group.forEach((child, i) => { - let node: Opt<Node>; - if ('inlineObjectId' in child) { - node = imageNode(state.schema, inlineObjectMap.get(child.inlineObjectId!)!, textNote); - } else if ('content' in child && (i !== group.length - 1 || child.content!.removeTrailingNewlines().length)) { - node = paragraphNode(state.schema, [child]); - } - if (node) { - position += node.nodeSize; - nodes.push(node); - } - }); - } else { - const paragraph = paragraphNode(state.schema, element.contents); - nodes.push(paragraph); - position += paragraph.nodeSize; - } + const paragraph = paragraphNode(state.schema, element.contents); + nodes.push(paragraph); + position += paragraph.nodeSize; } } state = state.apply(state.tr.replaceWith(0, 2, nodes)); const sink = sinkListItem(state.schema.nodes.list_item); - const dispatcher = (tr: Transaction) => (state = state.apply(tr)); - for (const list of lists) { - for (const pos of indentMap.get(list)!) { + const dispatcher = (tr: Transaction) => { + state = state.apply(tr); + }; + lists.forEach(list => { + indentMap.get(list)?.forEach(pos => { const resolved = state.doc.resolve(pos.value); state = state.apply(state.tr.setSelection(new TextSelection(resolved))); for (let i = 0; i < pos.sinks; i++) { sink(state, dispatcher); } - } - } + }); + }); return { title, text, state }; }; @@ -233,7 +238,7 @@ export namespace RichTextUtils { const parseLists = (paragraphs: ListGroup) => { const groups: PreparedParagraphs = []; let group: ListGroup = []; - for (const paragraph of paragraphs) { + paragraphs.forEach(paragraph => { if (paragraph.bullet !== undefined) { group.push(paragraph); } else { @@ -243,26 +248,22 @@ export namespace RichTextUtils { } groups.push(paragraph); } - } + }); group.length && groups.push(group); return groups; }; - const listItem = (schema: any, runs: docs_v1.Schema$TextRun[]): Node => { - return schema.node('list_item', null, paragraphNode(schema, runs)); - }; + const listItem = (lschema: any, runs: docsV1.Schema$TextRun[]): Node => lschema.node('list_item', null, paragraphNode(lschema, runs)); - const list = (schema: any, items: Node[]): Node => { - return schema.node('ordered_list', { mapStyle: 'bullet' }, items); - }; + const list = (lschema: any, items: Node[]): Node => lschema.node('ordered_list', { mapStyle: 'bullet' }, items); - const paragraphNode = (schema: any, runs: docs_v1.Schema$TextRun[]): Node => { - const children = runs.map(run => textNode(schema, run)).filter(child => child !== undefined); + const paragraphNode = (lschema: any, runs: docsV1.Schema$TextRun[]): Node => { + const children = runs.map(run => textNode(lschema, run)).filter(child => child !== undefined); const fragment = children.length ? Fragment.from(children) : undefined; - return schema.node('paragraph', null, fragment); + return lschema.node('paragraph', null, fragment); }; - const imageNode = (schema: any, image: ImageTemplate, textNote: Doc) => { + const imageNode = (lschema: any, image: ImageTemplate, textNote: Doc) => { const { url: src, width, agnostic } = image; let docId: string; const guid = Utils.GenerateDeterministicGuid(agnostic); @@ -275,30 +276,30 @@ export namespace RichTextUtils { } else { docId = backingDocId; } - return schema.node('image', { src, agnostic, width, docId, float: null }); + return lschema.node('image', { src, agnostic, width, docId, float: null }); }; - const textNode = (schema: any, run: docs_v1.Schema$TextRun) => { + const textNode = (lschema: any, run: docsV1.Schema$TextRun) => { const text = run.content!.removeTrailingNewlines(); - return text.length ? schema.text(text, styleToMarks(schema, run.textStyle)) : undefined; + return text.length ? lschema.text(text, styleToMarks(lschema, run.textStyle)) : undefined; }; - const StyleToMark = new Map<keyof docs_v1.Schema$TextStyle, keyof typeof schema.marks>([ + const StyleToMark = new Map<keyof docsV1.Schema$TextStyle, keyof typeof schema.marks>([ ['bold', 'strong'], ['italic', 'em'], ['foregroundColor', 'pFontColor'], ['fontSize', 'pFontSize'], ]); - const styleToMarks = (schema: any, textStyle?: docs_v1.Schema$TextStyle) => { + const styleToMarks = (lschema: any, textStyle?: docsV1.Schema$TextStyle) => { if (!textStyle) { return undefined; } const marks: Mark[] = []; Object.keys(textStyle).forEach(key => { - let value: any; - const targeted = key as keyof docs_v1.Schema$TextStyle; - if ((value = textStyle[targeted])) { + const targeted = key as keyof docsV1.Schema$TextStyle; + const value = textStyle[targeted] as any; + if (value) { const attributes: any = {}; let converted = StyleToMark.get(targeted) || targeted; @@ -315,20 +316,20 @@ export namespace RichTextUtils { converted = ImportFontFamilyMapping.get(value.fontFamily) || 'timesNewRoman'; } - const mapped = schema.marks[converted]; + const mapped = lschema.marks[converted]; if (!mapped) { alert(`No mapping found for ${converted}!`); return; } - const mark = schema.mark(mapped, attributes); + const mark = lschema.mark(mapped, attributes); mark && marks.push(mark); } }); return marks; }; - const MarkToStyle = new Map<keyof typeof schema.marks, keyof docs_v1.Schema$TextStyle>([ + const MarkToStyle = new Map<keyof typeof schema.marks, keyof docsV1.Schema$TextStyle>([ ['strong', 'bold'], ['em', 'italic'], ['pFontColor', 'foregroundColor'], @@ -360,16 +361,18 @@ export namespace RichTextUtils { const ignored = ['user_mark']; - const marksToStyle = async (nodes: (Node | null)[]): Promise<docs_v1.Schema$Request[]> => { - const requests: docs_v1.Schema$Request[] = []; + const marksToStyle = async (nodes: (Node | null)[]): Promise<docsV1.Schema$Request[]> => { + const requests: docsV1.Schema$Request[] = []; let position = 1; + // eslint-disable-next-line no-restricted-syntax for (const node of nodes) { if (node === null) { position += 2; + // eslint-disable-next-line no-continue continue; } const { marks, attrs, nodeSize } = node; - const textStyle: docs_v1.Schema$TextStyle = {}; + const textStyle: docsV1.Schema$TextStyle = {}; const information: LinkInformation = { startIndex: position, endIndex: position + nodeSize, @@ -377,36 +380,45 @@ export namespace RichTextUtils { }; let mark: Mark; const markMap = BuildMarkMap(marks); + // eslint-disable-next-line no-restricted-syntax for (const markName of Object.keys(schema.marks)) { + // eslint-disable-next-line no-cond-assign if (ignored.includes(markName) || !(mark = markMap[markName])) { + // eslint-disable-next-line no-continue continue; } - let converted = MarkToStyle.get(markName) || (markName as keyof docs_v1.Schema$TextStyle); + let converted = MarkToStyle.get(markName) || (markName as keyof docsV1.Schema$TextStyle); let value: any = true; if (!converted) { + // eslint-disable-next-line no-continue continue; } + // eslint-disable-next-line @typescript-eslint/no-shadow const { attrs } = mark; switch (converted) { case 'link': - let url = attrs.allLinks.length ? attrs.allLinks[0].href : ''; - const delimiter = '/doc/'; - const alreadyShared = '?sharing=true'; - if (new RegExp(window.location.origin + delimiter).test(url) && !url.endsWith(alreadyShared)) { - const linkDoc = await DocServer.GetRefField(url.split(delimiter)[1]); - if (linkDoc instanceof Doc) { - let exported = (await Cast(linkDoc.link_anchor_2, Doc))!; - if (!exported.customLayout) { - exported = Doc.MakeEmbedding(exported); - DocUtils.makeCustomViewClicked(exported, Docs.Create.FreeformDocument); - linkDoc.link_anchor_2 = exported; + { + let url = attrs.allLinks.length ? attrs.allLinks[0].href : ''; + const docDelimeter = '/doc/'; + const alreadyShared = '?sharing=true'; + if (new RegExp(window.location.origin + docDelimeter).test(url) && !url.endsWith(alreadyShared)) { + // eslint-disable-next-line no-await-in-loop + const linkDoc = await DocServer.GetRefField(url.split(docDelimeter)[1]); + if (linkDoc instanceof Doc) { + // eslint-disable-next-line no-await-in-loop + let exported = (await Cast(linkDoc.link_anchor_2, Doc))!; + if (!exported.customLayout) { + exported = Doc.MakeEmbedding(exported); + DocUtils.makeCustomViewClicked(exported, Docs.Create.FreeformDocument); + linkDoc.link_anchor_2 = exported; + } + url = ClientUtils.shareUrl(exported[Id]); } - url = Utils.shareUrl(exported[Id]); } + value = { url }; + textStyle.foregroundColor = fromRgb.blue; + textStyle.bold = true; } - value = { url }; - textStyle.foregroundColor = fromRgb.blue; - textStyle.bold = true; break; case 'fontSize': value = { magnitude: attrs.fontSize, unit: 'PT' }; @@ -416,9 +428,11 @@ export namespace RichTextUtils { break; case 'weightedFontFamily': value = { fontFamily: ExportFontFamilyMapping.get(markName) }; + break; + default: } - let matches: RegExpExecArray | null; - if ((matches = /p(\d+)/g.exec(markName)) !== null) { + const matches = /p(\d+)/g.exec(markName); + if (matches !== null) { converted = 'fontSize'; value = { magnitude: parseInt(matches[1].replace('px', '')), unit: 'PT' }; } @@ -428,7 +442,7 @@ export namespace RichTextUtils { requests.push(EncodeStyleUpdate(information)); } if (node.type.name === 'image') { - const width = attrs.width; + const { width } = attrs; requests.push( await EncodeImage({ startIndex: position + nodeSize - 1, @@ -444,14 +458,16 @@ export namespace RichTextUtils { const BuildMarkMap = (marks: readonly Mark[]) => { const markMap: { [type: string]: Mark } = {}; - marks.forEach(mark => (markMap[mark.type.name] = mark)); + marks.forEach(mark => { + markMap[mark.type.name] = mark; + }); return markMap; }; interface LinkInformation { startIndex: number; endIndex: number; - textStyle: docs_v1.Schema$TextStyle; + textStyle: docsV1.Schema$TextStyle; } interface ImageInformation { @@ -461,36 +477,34 @@ export namespace RichTextUtils { } namespace fromRgb { - export const convert = (red: number, green: number, blue: number): docs_v1.Schema$OptionalColor => { - return { - color: { - rgbColor: { - red: red / 255, - green: green / 255, - blue: blue / 255, - }, + export const convert = (red: number, green: number, blue: number): docsV1.Schema$OptionalColor => ({ + color: { + rgbColor: { + red: red / 255, + green: green / 255, + blue: blue / 255, }, - }; - }; + }, + }); export const red = convert(255, 0, 0); export const green = convert(0, 255, 0); export const blue = convert(0, 0, 255); } - const fromHex = (color: string): docs_v1.Schema$OptionalColor => { + const fromHex = (color: string): docsV1.Schema$OptionalColor => { const c = DashColor(color); return fromRgb.convert(c.red(), c.green(), c.blue()); }; - const EncodeStyleUpdate = (information: LinkInformation): docs_v1.Schema$Request => { + const EncodeStyleUpdate = (information: LinkInformation): docsV1.Schema$Request => { const { startIndex, endIndex, textStyle } = information; return { updateTextStyle: { fields: '*', range: { startIndex, endIndex }, textStyle, - } as docs_v1.Schema$UpdateTextStyleRequest, + } as docsV1.Schema$UpdateTextStyleRequest, }; }; |