├── .stylelintignore ├── _config.yml ├── .gitattribute ├── src ├── ui │ ├── czi-frag.css │ ├── isOffline.js │ ├── uuid.js │ ├── czi-custom-menu.css │ ├── czi-bookmark-view.css │ ├── Frag.js │ ├── isReactClass.js │ ├── CustomMenu.js │ ├── KeyCodes.js │ ├── icon-font.css │ ├── htmlElementToRect.js │ ├── czi-table-cell-menu.css │ ├── listType.css │ ├── czi-body-layout-editor.css │ ├── czi-inline-editor.css │ ├── czi-custom-menu-button.css │ ├── czi-custom-scrollbar.css │ ├── isElementFullyVisible.js │ ├── toHexColor.js │ ├── LoadingIndicator.js │ ├── czi-heading.css │ ├── czi-selection-placeholder.css │ ├── handleEditorDrop.js │ ├── czi-custom-menu-item.css │ ├── CustomEditorView.js │ ├── handleEditorPaste.js │ ├── czi-table-grid-size-editor.css │ ├── czi-image-url-editor.css │ ├── czi-cursor-placeholder.css │ ├── handleEditorKeyDown.js │ ├── CustomMenuItem.js │ ├── czi-image-upload-placeholder.css │ ├── findActiveFontType.js │ ├── TableNodeView.js │ ├── PasteMenu.js │ ├── bindScrollHandler.js │ ├── injectStyleSheet.js │ ├── toCSSColor.js │ ├── czi-math-view.css │ ├── czi-loading-indicator.css │ ├── TableCellMenu.js │ ├── czi-image-upload-editor.css │ ├── canUseCSSFont.js │ ├── LinkTooltip.js │ ├── toCSSLineSpacing.js │ ├── ImageInlineEditor.js │ ├── AlertInfo.js │ ├── FontTypeCommandMenuButton.js │ ├── findActiveFontSize.js │ ├── CustomRadioButton.js │ ├── czi-icon.css │ ├── FontSizeCommandMenuButton.js │ ├── czi-editor-frameset.css │ ├── czi-custom-radio-button.css │ ├── czi-vars.css │ ├── BookmarkNodeView.js │ ├── CommandButton.js │ ├── czi-table.css │ ├── EditorFrameset.js │ ├── ListTypeMenu.js │ └── ListTypeCommandButton.js ├── fonts │ ├── acme │ │ └── Acme.woff2 │ ├── times │ │ ├── Times-L.woff2 │ │ └── Times-LE.woff2 │ ├── tahoma │ │ ├── Tahoma-C.woff2 │ │ ├── Tahoma-G.woff2 │ │ ├── Tahoma-L.woff2 │ │ ├── Tahoma-CE.woff2 │ │ ├── Tahoma-GE.woff2 │ │ └── Tahoma-LE.woff2 │ ├── aclonica │ │ └── Aclonica.woff2 │ ├── georgia │ │ ├── Georgia-C.woff2 │ │ ├── Georgia-CE.woff2 │ │ ├── Georgia-G.woff2 │ │ ├── Georgia-GE.woff2 │ │ ├── Georgia-L.woff2 │ │ └── Georgia-LE.woff2 │ ├── verdana │ │ ├── Verdana-C.woff2 │ │ ├── Verdana-CE.woff2 │ │ ├── Verdana-G.woff2 │ │ ├── Verdana-GE.woff2 │ │ ├── Verdana-L.woff2 │ │ ├── Verdana-LE.woff2 │ │ └── Verdana-V.woff2 │ ├── arial-black │ │ ├── ArialBlack-G.woff2 │ │ ├── ArialBlack-L.woff2 │ │ └── ArialBlack-LE.woff2 │ ├── courier-new │ │ └── CourierNew-L.woff2 │ ├── alegreya │ │ ├── Alegreya-Regular-C.woff2 │ │ ├── Alegreya-Regular-CE.woff2 │ │ ├── Alegreya-Regular-G.woff2 │ │ ├── Alegreya-Regular-GE.woff2 │ │ ├── Alegreya-Regular-L.woff2 │ │ ├── Alegreya-Regular-LE.woff2 │ │ └── Alegreya-Regular-V.woff2 │ ├── times-new-roman │ │ ├── TimesNewRoman-C.woff2 │ │ ├── TimesNewRoman-CE.woff2 │ │ ├── TimesNewRoman-G.woff2 │ │ ├── TimesNewRoman-GE.woff2 │ │ ├── TimesNewRoman-L.woff2 │ │ ├── TimesNewRoman-LE.woff2 │ │ └── TimesNewRoman-V.woff2 │ └── material-icons │ │ └── MaterialIcons-Regular.ttf ├── browser.js ├── TextNodeSpec.js ├── EditorState.js ├── client │ ├── licit.css │ ├── throttle.js │ ├── Reporter.js │ ├── http.js │ └── SimpleConnector.js ├── convertToJSON.js ├── uuid.js ├── EditorPlugins.js ├── CodeMarkSpec.js ├── sanitizeURL.js ├── HardBreakNodeSpec.js ├── lookUpElement.js ├── EditorSchema.js ├── TextNoWrapMarkSpec.js ├── nodeAt.js ├── TablePlugins.js ├── isTableNode.js ├── toSafeHTMLDocument.js ├── hyphenize.js ├── StyleView.js ├── index.js ├── convertFromHTML.js ├── WebFontLoader.js ├── TextSelectionMarkSpec.js ├── patchParagraphElements.js ├── convertToCSSPTValue.js ├── CodeBlockNodeSpec.js ├── TextUnderlineMarkSpec.js ├── TextSuperMarkSpec.js ├── StrikeMarkSpec.js ├── findActiveMark.js ├── TextSubMarkSpec.js ├── createEmptyEditorState.js ├── joinDown.js ├── patchBreakElements.js ├── BlockquoteNodeSpec.js ├── HangingIndentMarkSpec.js ├── MarkNames.js ├── NodeNames.js ├── HorizontalRuleNodeSpec.js ├── toClosestFontPtSize.js ├── isEditorStateEmpty.js ├── EMMarkSpec.js ├── StrongMarkSpec.js ├── blockQuoteInputRule.js ├── BookmarkNodeSpec.js ├── LinkMarkSpec.js ├── TextColorMarkSpec.js ├── convertFromDOMElement.js ├── joinUp.js ├── rebaseDocWithSteps.js ├── SpacerMarkSpec.js ├── HistoryRedoCommand.js ├── HistoryUndoCommand.js ├── HeadingNodeSpec.js ├── PrintCommand.js ├── patchAnchorElements.js ├── TextHighlightMarkSpec.js ├── ListSplitCommand.js ├── convertFromJSON.js ├── joinListNode.js ├── ListItemNodeSpec.js ├── insertTable.js ├── FontSizeMarkSpec.js ├── BlockquoteToggleCommand.js ├── createCommand.js ├── BulletListNodeSpec.js ├── styles.css ├── OverrideMarkSpec.js ├── DocNodeSpec.js ├── HTMLMutator.js ├── Types.js ├── MarksClearCommand.js ├── EditorNodes.js ├── CodeBlockCommand.js ├── HorizontalRuleCommand.js ├── BlockquoteInsertNewLineCommand.js ├── buildEditorPlugins.js ├── patchMathElements.js ├── EditorPageLayoutPlugin.js ├── findActionableCell.js ├── TableCellColorCommand.js └── ListItemInsertNewLineCommand.js ├── lint.sh ├── .prettierignore ├── flow-typed ├── docs-editor.js ├── draft-js.js ├── draft-convert.js ├── uuid.js ├── flatted.js ├── create-emotion.js ├── prosemirror-collab.js ├── prosemirror-keymap.js ├── prosemirror-model.js ├── prosemirror-state.js ├── prosemirror-tables.js ├── prosemirror-utils.js ├── prosemirror-view.js ├── prosemirror-commands.js ├── prosemirror-history.js ├── prosemirror-dropcursor.js ├── prosemirror-gapcursor.js ├── prosemirror-inputrules.js ├── prosemirror-transform.js ├── resize-observer-polyfill.js ├── @modusoperandilicit-customstyles.js ├── @modusoperandilicit-ui-commands.js ├── @modusoperandilicit-doc-attrs-step.js └── katex.js ├── .dockerignore ├── .prettierrc ├── jest.setup.js ├── .gitignore ├── licit ├── index.html └── server │ └── collab │ ├── start.js │ └── route.js ├── utils ├── build_bin.js ├── env.js └── build_web_server.js ├── scripts ├── env.js ├── build_bin.js ├── ci_check_dist.sh └── webserver.js ├── .github ├── release.yml ├── dependabot.yml └── workflows │ ├── dependencycheck.yml │ ├── version-bump.yml │ └── publish.yml ├── .stylelintrc.json ├── sonar-project.properties ├── .travis.yml ├── run_image_server.py ├── run_collab_server.py ├── run_web_server.py ├── tsconfig.json ├── style-service.Dockerfile ├── .flowconfig ├── LICENSE ├── .babelrc ├── babel.config.json ├── CODEOWNERS └── eslint.config.mjs /.stylelintignore: -------------------------------------------------------------------------------- 1 | /bin 2 | /dist -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-midnight -------------------------------------------------------------------------------- /.gitattribute: -------------------------------------------------------------------------------- 1 | dist/** linguist-generated=true -------------------------------------------------------------------------------- /src/ui/czi-frag.css: -------------------------------------------------------------------------------- 1 | .czi-frag { 2 | display: contents; 3 | } 4 | -------------------------------------------------------------------------------- /lint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | node node_modules/eslint/bin/eslint.js --fix src/ 4 | 5 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | /package.json 2 | /bin 3 | /dist 4 | /.eslintrc 5 | /.vscode 6 | /.stylelintrc.json -------------------------------------------------------------------------------- /flow-typed/docs-editor.js: -------------------------------------------------------------------------------- 1 | declare module 'czi-rte' { 2 | declare module.exports: any; 3 | } 4 | -------------------------------------------------------------------------------- /flow-typed/draft-js.js: -------------------------------------------------------------------------------- 1 | declare module 'draft-js' { 2 | declare module.exports: any; 3 | } 4 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in the project 2 | ** 3 | # Except servers folder 4 | !servers 5 | -------------------------------------------------------------------------------- /src/fonts/acme/Acme.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MO-Movia/licit/HEAD/src/fonts/acme/Acme.woff2 -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "bracketSpacing": true, 3 | "singleQuote": true, 4 | "trailingComma": "es5" 5 | } -------------------------------------------------------------------------------- /flow-typed/draft-convert.js: -------------------------------------------------------------------------------- 1 | declare module 'draft-convert' { 2 | declare module.exports: any; 3 | } 4 | -------------------------------------------------------------------------------- /flow-typed/uuid.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | declare module 'uuid' { 4 | declare module.exports: any; 5 | } 6 | -------------------------------------------------------------------------------- /src/fonts/times/Times-L.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MO-Movia/licit/HEAD/src/fonts/times/Times-L.woff2 -------------------------------------------------------------------------------- /flow-typed/flatted.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | declare module 'flatted' { 4 | declare module.exports: any; 5 | } 6 | -------------------------------------------------------------------------------- /src/fonts/tahoma/Tahoma-C.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MO-Movia/licit/HEAD/src/fonts/tahoma/Tahoma-C.woff2 -------------------------------------------------------------------------------- /src/fonts/tahoma/Tahoma-G.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MO-Movia/licit/HEAD/src/fonts/tahoma/Tahoma-G.woff2 -------------------------------------------------------------------------------- /src/fonts/tahoma/Tahoma-L.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MO-Movia/licit/HEAD/src/fonts/tahoma/Tahoma-L.woff2 -------------------------------------------------------------------------------- /src/fonts/times/Times-LE.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MO-Movia/licit/HEAD/src/fonts/times/Times-LE.woff2 -------------------------------------------------------------------------------- /src/fonts/aclonica/Aclonica.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MO-Movia/licit/HEAD/src/fonts/aclonica/Aclonica.woff2 -------------------------------------------------------------------------------- /src/fonts/georgia/Georgia-C.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MO-Movia/licit/HEAD/src/fonts/georgia/Georgia-C.woff2 -------------------------------------------------------------------------------- /src/fonts/georgia/Georgia-CE.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MO-Movia/licit/HEAD/src/fonts/georgia/Georgia-CE.woff2 -------------------------------------------------------------------------------- /src/fonts/georgia/Georgia-G.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MO-Movia/licit/HEAD/src/fonts/georgia/Georgia-G.woff2 -------------------------------------------------------------------------------- /src/fonts/georgia/Georgia-GE.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MO-Movia/licit/HEAD/src/fonts/georgia/Georgia-GE.woff2 -------------------------------------------------------------------------------- /src/fonts/georgia/Georgia-L.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MO-Movia/licit/HEAD/src/fonts/georgia/Georgia-L.woff2 -------------------------------------------------------------------------------- /src/fonts/georgia/Georgia-LE.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MO-Movia/licit/HEAD/src/fonts/georgia/Georgia-LE.woff2 -------------------------------------------------------------------------------- /src/fonts/tahoma/Tahoma-CE.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MO-Movia/licit/HEAD/src/fonts/tahoma/Tahoma-CE.woff2 -------------------------------------------------------------------------------- /src/fonts/tahoma/Tahoma-GE.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MO-Movia/licit/HEAD/src/fonts/tahoma/Tahoma-GE.woff2 -------------------------------------------------------------------------------- /src/fonts/tahoma/Tahoma-LE.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MO-Movia/licit/HEAD/src/fonts/tahoma/Tahoma-LE.woff2 -------------------------------------------------------------------------------- /src/fonts/verdana/Verdana-C.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MO-Movia/licit/HEAD/src/fonts/verdana/Verdana-C.woff2 -------------------------------------------------------------------------------- /src/fonts/verdana/Verdana-CE.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MO-Movia/licit/HEAD/src/fonts/verdana/Verdana-CE.woff2 -------------------------------------------------------------------------------- /src/fonts/verdana/Verdana-G.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MO-Movia/licit/HEAD/src/fonts/verdana/Verdana-G.woff2 -------------------------------------------------------------------------------- /src/fonts/verdana/Verdana-GE.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MO-Movia/licit/HEAD/src/fonts/verdana/Verdana-GE.woff2 -------------------------------------------------------------------------------- /src/fonts/verdana/Verdana-L.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MO-Movia/licit/HEAD/src/fonts/verdana/Verdana-L.woff2 -------------------------------------------------------------------------------- /src/fonts/verdana/Verdana-LE.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MO-Movia/licit/HEAD/src/fonts/verdana/Verdana-LE.woff2 -------------------------------------------------------------------------------- /src/fonts/verdana/Verdana-V.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MO-Movia/licit/HEAD/src/fonts/verdana/Verdana-V.woff2 -------------------------------------------------------------------------------- /src/browser.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | const browser = { 4 | isMac: () => true, 5 | }; 6 | 7 | export default browser; 8 | -------------------------------------------------------------------------------- /flow-typed/create-emotion.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | declare module 'create-emotion' { 4 | declare module.exports: any; 5 | } 6 | -------------------------------------------------------------------------------- /flow-typed/prosemirror-collab.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | declare module 'prosemirror-collab' { 4 | declare module.exports: any; 5 | } 6 | -------------------------------------------------------------------------------- /flow-typed/prosemirror-keymap.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | declare module 'prosemirror-keymap' { 4 | declare module.exports: any; 5 | } 6 | -------------------------------------------------------------------------------- /flow-typed/prosemirror-model.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | declare module 'prosemirror-model' { 4 | declare module.exports: any; 5 | } 6 | -------------------------------------------------------------------------------- /flow-typed/prosemirror-state.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | declare module 'prosemirror-state' { 4 | declare module.exports: any; 5 | } 6 | -------------------------------------------------------------------------------- /flow-typed/prosemirror-tables.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | declare module 'prosemirror-tables' { 4 | declare module.exports: any; 5 | } 6 | -------------------------------------------------------------------------------- /flow-typed/prosemirror-utils.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | declare module 'prosemirror-utils' { 4 | declare module.exports: any; 5 | } 6 | -------------------------------------------------------------------------------- /flow-typed/prosemirror-view.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | declare module 'prosemirror-view' { 4 | declare module.exports: any; 5 | } 6 | -------------------------------------------------------------------------------- /src/TextNodeSpec.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | const TextNodeSpec = { 4 | group: 'inline', 5 | }; 6 | 7 | export default TextNodeSpec; 8 | -------------------------------------------------------------------------------- /src/fonts/arial-black/ArialBlack-G.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MO-Movia/licit/HEAD/src/fonts/arial-black/ArialBlack-G.woff2 -------------------------------------------------------------------------------- /src/fonts/arial-black/ArialBlack-L.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MO-Movia/licit/HEAD/src/fonts/arial-black/ArialBlack-L.woff2 -------------------------------------------------------------------------------- /src/fonts/arial-black/ArialBlack-LE.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MO-Movia/licit/HEAD/src/fonts/arial-black/ArialBlack-LE.woff2 -------------------------------------------------------------------------------- /src/fonts/courier-new/CourierNew-L.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MO-Movia/licit/HEAD/src/fonts/courier-new/CourierNew-L.woff2 -------------------------------------------------------------------------------- /flow-typed/prosemirror-commands.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | declare module 'prosemirror-commands' { 4 | declare module.exports: any; 5 | } 6 | -------------------------------------------------------------------------------- /flow-typed/prosemirror-history.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | declare module 'prosemirror-history' { 4 | declare module.exports: any; 5 | } 6 | -------------------------------------------------------------------------------- /src/fonts/alegreya/Alegreya-Regular-C.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MO-Movia/licit/HEAD/src/fonts/alegreya/Alegreya-Regular-C.woff2 -------------------------------------------------------------------------------- /src/fonts/alegreya/Alegreya-Regular-CE.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MO-Movia/licit/HEAD/src/fonts/alegreya/Alegreya-Regular-CE.woff2 -------------------------------------------------------------------------------- /src/fonts/alegreya/Alegreya-Regular-G.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MO-Movia/licit/HEAD/src/fonts/alegreya/Alegreya-Regular-G.woff2 -------------------------------------------------------------------------------- /src/fonts/alegreya/Alegreya-Regular-GE.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MO-Movia/licit/HEAD/src/fonts/alegreya/Alegreya-Regular-GE.woff2 -------------------------------------------------------------------------------- /src/fonts/alegreya/Alegreya-Regular-L.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MO-Movia/licit/HEAD/src/fonts/alegreya/Alegreya-Regular-L.woff2 -------------------------------------------------------------------------------- /src/fonts/alegreya/Alegreya-Regular-LE.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MO-Movia/licit/HEAD/src/fonts/alegreya/Alegreya-Regular-LE.woff2 -------------------------------------------------------------------------------- /src/fonts/alegreya/Alegreya-Regular-V.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MO-Movia/licit/HEAD/src/fonts/alegreya/Alegreya-Regular-V.woff2 -------------------------------------------------------------------------------- /flow-typed/prosemirror-dropcursor.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | declare module 'prosemirror-dropcursor' { 4 | declare module.exports: any; 5 | } 6 | -------------------------------------------------------------------------------- /flow-typed/prosemirror-gapcursor.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | declare module 'prosemirror-gapcursor' { 4 | declare module.exports: any; 5 | } 6 | -------------------------------------------------------------------------------- /flow-typed/prosemirror-inputrules.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | declare module 'prosemirror-inputrules' { 4 | declare module.exports: any; 5 | } 6 | -------------------------------------------------------------------------------- /flow-typed/prosemirror-transform.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | declare module 'prosemirror-transform' { 4 | declare module.exports: any; 5 | } 6 | -------------------------------------------------------------------------------- /flow-typed/resize-observer-polyfill.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | declare module 'resize-observer-polyfill' { 4 | declare module.exports: any; 5 | } 6 | -------------------------------------------------------------------------------- /src/fonts/times-new-roman/TimesNewRoman-C.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MO-Movia/licit/HEAD/src/fonts/times-new-roman/TimesNewRoman-C.woff2 -------------------------------------------------------------------------------- /src/fonts/times-new-roman/TimesNewRoman-CE.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MO-Movia/licit/HEAD/src/fonts/times-new-roman/TimesNewRoman-CE.woff2 -------------------------------------------------------------------------------- /src/fonts/times-new-roman/TimesNewRoman-G.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MO-Movia/licit/HEAD/src/fonts/times-new-roman/TimesNewRoman-G.woff2 -------------------------------------------------------------------------------- /src/fonts/times-new-roman/TimesNewRoman-GE.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MO-Movia/licit/HEAD/src/fonts/times-new-roman/TimesNewRoman-GE.woff2 -------------------------------------------------------------------------------- /src/fonts/times-new-roman/TimesNewRoman-L.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MO-Movia/licit/HEAD/src/fonts/times-new-roman/TimesNewRoman-L.woff2 -------------------------------------------------------------------------------- /src/fonts/times-new-roman/TimesNewRoman-LE.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MO-Movia/licit/HEAD/src/fonts/times-new-roman/TimesNewRoman-LE.woff2 -------------------------------------------------------------------------------- /src/fonts/times-new-roman/TimesNewRoman-V.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MO-Movia/licit/HEAD/src/fonts/times-new-roman/TimesNewRoman-V.woff2 -------------------------------------------------------------------------------- /jest.setup.js: -------------------------------------------------------------------------------- 1 | // needed to mock this due to execute during loading 2 | document.execCommand = document.execCommand || function execCommandMock() {}; 3 | -------------------------------------------------------------------------------- /src/fonts/material-icons/MaterialIcons-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MO-Movia/licit/HEAD/src/fonts/material-icons/MaterialIcons-Regular.ttf -------------------------------------------------------------------------------- /flow-typed/@modusoperandilicit-customstyles.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | declare module '@modusoperandi/licit-customstyles' { 4 | declare module.exports: any; 5 | } 6 | -------------------------------------------------------------------------------- /flow-typed/@modusoperandilicit-ui-commands.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | declare module '@modusoperandi/licit-ui-commands' { 4 | declare module.exports: any; 5 | } 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | bin 3 | node_modules 4 | servers 5 | *.pyc 6 | *.code-workspace 7 | .vscode/ 8 | dist 9 | src/coverage 10 | /coverage 11 | *.tgz 12 | -------------------------------------------------------------------------------- /flow-typed/@modusoperandilicit-doc-attrs-step.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | declare module '@modusoperandi/licit-doc-attrs-step' { 4 | declare module.exports: any; 5 | } 6 | -------------------------------------------------------------------------------- /flow-typed/katex.js: -------------------------------------------------------------------------------- 1 | declare module 'katex' { 2 | declare module.exports: any; 3 | } 4 | 5 | declare module 'katex/dist/katex.min.css' { 6 | declare module.exports: any; 7 | } -------------------------------------------------------------------------------- /src/EditorState.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { EditorState } from 'prosemirror-state'; 4 | 5 | const ProseMirrorEditorState = EditorState; 6 | 7 | export default ProseMirrorEditorState; 8 | -------------------------------------------------------------------------------- /licit/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |` element with a
8 | // `` element inside of it.
9 | const CodeBlockNodeSpec = {
10 | attrs: {
11 | id: { default: null },
12 | },
13 | content: 'inline*',
14 | group: 'block',
15 | marks: '_',
16 | code: true,
17 | defining: true,
18 | parseDOM: [{ tag: 'pre', preserveWhitespace: 'full' }],
19 | toDOM() {
20 | return PRE_DOM;
21 | },
22 | };
23 |
24 | export default CodeBlockNodeSpec;
25 |
--------------------------------------------------------------------------------
/src/TextUnderlineMarkSpec.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | import type { MarkSpec } from './Types.js';
4 |
5 | // https://bitbucket.org/atlassian/atlaskit/src/34facee3f461/packages/editor-core/src/schema/nodes/?at=master
6 | const TextUnderlineMarkSpec: MarkSpec = {
7 | attrs: {
8 | overridden: { default: false },
9 | },
10 | parseDOM: [
11 | {
12 | tag: 'u',
13 | getAttrs: (dom: HTMLElement) => {
14 | const _overridden = dom.getAttribute('overridden');
15 | return { overridden: _overridden === 'true' };
16 | }
17 | },
18 | ],
19 | toDOM(mark) {
20 | return ['u', { overridden: mark.attrs.overridden }, 0];
21 | },
22 | };
23 |
24 | export default TextUnderlineMarkSpec;
25 |
--------------------------------------------------------------------------------
/src/TextSuperMarkSpec.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | import type { MarkSpec } from './Types.js';
4 |
5 | const TextSuperMarkSpec: MarkSpec = {
6 | attrs: {
7 | overridden: { default: false },
8 | },
9 | parseDOM: [
10 | {
11 | tag: 'sup',
12 | getAttrs: (dom: HTMLElement) => {
13 | const _overridden = dom.getAttribute('overridden');
14 | return { overridden: _overridden === 'true' };
15 | }
16 | },
17 |
18 | {
19 | style: 'vertical-align',
20 | getAttrs: (value) => (value === 'sup' ? { overridden: true } : null),
21 | },
22 | ],
23 | toDOM(mark) {
24 | return ['sup', { overridden: mark.attrs.overridden }, 0];
25 | },
26 | };
27 |
28 | export default TextSuperMarkSpec;
29 |
--------------------------------------------------------------------------------
/src/StrikeMarkSpec.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | import type { MarkSpec } from './Types.js';
4 |
5 | // https://bitbucket.org/atlassian/atlaskit/src/34facee3f461/packages/editor-core/src/schema/nodes/?at=master
6 | const StrikeMarkSpec: MarkSpec = {
7 | attrs: {
8 | overridden: { default: false }, // Optional attribute for additional logic
9 | },
10 | parseDOM: [
11 | {
12 | tag: 'strike',
13 | getAttrs: (dom: HTMLElement) => {
14 | const _overridden = dom.getAttribute('overridden');
15 | return { overridden: _overridden === 'true' };
16 | }
17 | },
18 | ],
19 | toDOM(mark) {
20 | return ['strike', { overridden: mark.attrs.overridden }, 0];
21 | },
22 | };
23 |
24 | export default StrikeMarkSpec;
25 |
--------------------------------------------------------------------------------
/src/findActiveMark.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | import { Mark, MarkType, Node } from 'prosemirror-model';
4 |
5 | export default function findActiveMark(
6 | doc: Node,
7 | from: number,
8 | to: number,
9 | markType: MarkType
10 | ): ?Mark {
11 | let ii = from;
12 | if (doc.nodeSize <= 2) {
13 | return null;
14 | }
15 | const finder = (mark) => mark.type === markType;
16 | from = Math.max(2, from);
17 | to = Math.min(to, doc.nodeSize - 2);
18 |
19 | while (ii <= to) {
20 | const node = doc.nodeAt(ii);
21 | if (!node?.marks) {
22 | ii++;
23 | continue;
24 | }
25 | const mark = node.marks.find(finder);
26 | if (mark) {
27 | return mark;
28 | }
29 | ii++;
30 | }
31 | return null;
32 | }
33 |
--------------------------------------------------------------------------------
/src/TextSubMarkSpec.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | import type { MarkSpec } from './Types.js';
4 |
5 | const TextSubMarkSpec: MarkSpec = {
6 | attrs: {
7 | overridden: { default: false },
8 | },
9 | parseDOM: [
10 | {
11 | tag: 'sub',
12 | priority: 150,
13 | getAttrs: (dom: HTMLElement) => {
14 | const _overridden = dom.getAttribute('overridden');
15 | return { overridden: _overridden === 'true' };
16 | }
17 | },
18 | {
19 | style: 'vertical-align',
20 | getAttrs: (value) => (value === 'sub' ? { overridden: true } : null),
21 | },
22 | ],
23 | toDOM(mark) {
24 | return ['sub', { overridden: mark.attrs.overridden }, 0];
25 | },
26 | };
27 |
28 | export default TextSubMarkSpec;
29 |
--------------------------------------------------------------------------------
/src/ui/czi-heading.css:
--------------------------------------------------------------------------------
1 | .ProseMirror h1,
2 | .ProseMirror h2,
3 | .ProseMirror h3,
4 | .ProseMirror h4,
5 | .ProseMirror h5,
6 | .ProseMirror h6 {
7 | font-weight: normal;
8 | line-height: var(--czi-content-line-height);
9 | orphans: 2;
10 | widows: 2;
11 | }
12 |
13 | .ProseMirror h1 {
14 | font-size: 20pt;
15 | }
16 |
17 | .ProseMirror h2 {
18 | font-size: 18pt;
19 | }
20 |
21 | .ProseMirror h3 {
22 | font-size: 16pt;
23 | }
24 |
25 | .ProseMirror h4 {
26 | color: rgb(67, 67, 67);
27 | font-size: 14pt;
28 | }
29 |
30 | .ProseMirror h5 {
31 | color: rgb(102, 102, 102);
32 | font-size: 11pt;
33 | font-weight: 400;
34 | }
35 |
36 | .ProseMirror h6 {
37 | color: rgb(102, 102, 102);
38 | font-size: 11pt;
39 | font-weight: 400;
40 | }
41 |
--------------------------------------------------------------------------------
/src/ui/czi-selection-placeholder.css:
--------------------------------------------------------------------------------
1 | @import 'czi-vars.css';
2 |
3 | .czi-selection-placeholder {
4 | -webkit-animation-direction: alternate;
5 | animation-direction: alternate;
6 | -webkit-animation-duration: 0.5s;
7 | animation-duration: 0.5s;
8 | -webkit-animation-iteration-count: infinite;
9 | animation-iteration-count: infinite;
10 | -webkit-animation-name: czi_selection_blink;
11 | animation-name: czi_selection_blink;
12 | -webkit-animation-timing-function: ease-in-out;
13 | animation-timing-function: ease-in-out;
14 | background-color: var(--czi-selection-highlight-color-dark) !important;
15 | }
16 |
17 | @-webkit-keyframes czi_selection_blink {
18 | from {
19 | opacity: 1;
20 | }
21 | to {
22 | opacity: 0.7;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/run_web_server.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | import os
5 | import platform
6 | import logging
7 | import socket
8 | from subprocess import check_output
9 |
10 | # [FS][03-MAR-2020]
11 | # IRAD-892 Correct licit windows build
12 | # gracefully handling commands WRT OS.
13 | hostname = socket.gethostname()
14 | IPAddr = socket.gethostbyname(hostname)
15 | port = '3001'
16 |
17 | if platform.system()=="Windows": cmd = 'node utils/build_web_server.js ' + ' PORT=' + port + ' IP='+ IPAddr
18 | else: cmd = 'PORT=' + port + ' IP=' + IPAddr + ' node utils/build_web_server.js'
19 |
20 | print('=' * 80)
21 | print(cmd)
22 | print('=' * 80)
23 | print('run web server at http://' + IPAddr + ':' + port)
24 | print('=' * 80)
25 | os.system(cmd)
26 |
--------------------------------------------------------------------------------
/src/ui/handleEditorDrop.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | import { EditorView } from 'prosemirror-view';
4 |
5 | import { uploadImageFiles } from '../ImageUploadPlaceholderPlugin.js';
6 |
7 | // https://prosemirror.net/examples/upload/
8 | export default function handleEditorDrop(
9 | view: EditorView,
10 | event: DragEvent
11 | ): boolean {
12 | const { dataTransfer } = event;
13 | if (!dataTransfer) {
14 | return false;
15 | }
16 | const { files } = dataTransfer;
17 | if (!files?.length) {
18 | return false;
19 | }
20 |
21 | const filesList = Array.from(files);
22 | const coords = { x: event.clientX, y: event.clientY };
23 | if (uploadImageFiles(view, filesList, coords)) {
24 | event.preventDefault();
25 | return true;
26 | }
27 | return false;
28 | }
29 |
--------------------------------------------------------------------------------
/src/ui/czi-custom-menu-item.css:
--------------------------------------------------------------------------------
1 | .czi-custom-menu-item.czi-custom-button {
2 | border: none;
3 | border-radius: 0;
4 | display: block;
5 | margin: 0;
6 | padding-bottom: 8px;
7 | padding-left: 20px;
8 | padding-right: 20px;
9 | padding-top: 8px;
10 | }
11 |
12 | .czi-custom-menu-item.czi-custom-button:hover {
13 | background-color: var(--czi-button-hover-background-color);
14 | }
15 |
16 | /* [FS] IRAD-1044 2020-09-22
17 | To adjust the width of the custom style menu dropdown. */
18 |
19 | .custom-style-menu-item {
20 | width: 250px;
21 | }
22 |
23 | .czi-custom-menu-item-separator {
24 | background-color: var(--czi-button-hover-background-color);
25 | height: 1px;
26 | margin-bottom: 8px;
27 | margin-left: 0;
28 | margin-right: 0;
29 | margin-top: 8px;
30 | }
31 |
--------------------------------------------------------------------------------
/src/client/Reporter.js:
--------------------------------------------------------------------------------
1 | class Reporter {
2 | constructor() {
3 | this.setAt = 0;
4 | }
5 |
6 | clearState() {
7 | if (this.state) {
8 | this.state = this.node = null;
9 | this.setAt = 0;
10 | }
11 | }
12 |
13 | failure(err) {
14 | console.error('fail', err.toString());
15 | }
16 |
17 | delay(err) {
18 | if (this.state == 'fail') return;
19 | console.info('delay', err.toString());
20 | }
21 |
22 | show(type, message) {
23 | this.clearState();
24 | this.state = type;
25 | this.setAt = Date.now();
26 | }
27 |
28 | success() {
29 | if (this.state == 'fail' && this.setAt > Date.now() - 1000 * 10)
30 | setTimeout(() => this.success(), 5000);
31 | else this.clearState();
32 | }
33 | }
34 |
35 | export default Reporter;
36 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: "npm" # See documentation for possible values
9 | directory: "/" # Location of package manifests
10 | schedule:
11 | interval: weekly
12 | day: sunday
13 | # Raise pull requests for version updates
14 | # to npm against the `develop` branch
15 | target-branch: "develop"
16 | # Labels on pull requests for security and version updates
17 | labels:
18 | - "npm dependencies"
--------------------------------------------------------------------------------
/src/createEmptyEditorState.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | import { Schema } from 'prosemirror-model';
4 | import { EditorState, Plugin } from 'prosemirror-state';
5 | import convertFromJSON from './convertFromJSON.js';
6 | import EditorSchema from './EditorSchema.js';
7 |
8 | export const EMPTY_DOC_JSON = {
9 | type: 'doc',
10 | content: [
11 | {
12 | type: 'paragraph',
13 | }, // [FS] IRAD-1710 2022-03-04 - No text content needed
14 | ],
15 | };
16 |
17 | export default function createEmptyEditorState(
18 | schema: ?Schema,
19 | defaultSchema: ?Schema,
20 | plugins: Array
21 | ): EditorState {
22 | // TODO: Check if schema support doc and paragraph nodes.
23 | return convertFromJSON(
24 | EMPTY_DOC_JSON,
25 | schema,
26 | defaultSchema || EditorSchema,
27 | plugins
28 | );
29 | }
30 |
--------------------------------------------------------------------------------
/src/ui/CustomEditorView.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | import { EditorView } from 'prosemirror-view';
4 | import * as React from 'react';
5 |
6 | import type { DirectEditorProps, EditorRuntime } from '../Types.js';
7 |
8 |
9 | // https://github.com/ProseMirror/prosemirror-view/blob/master/src/index.js
10 | class CustomEditorView extends EditorView {
11 | disabled: boolean;
12 | placeholder: ?(string | React.Element);
13 | readOnly: boolean;
14 | runtime: ?EditorRuntime;
15 | constructor(place: HTMLElement, props: DirectEditorProps) {
16 | super(place, props);
17 | this.runtime = null;
18 | this.readOnly = true;
19 | this.disabled = true;
20 | this.placeholder = null;
21 | }
22 |
23 | destroy() {
24 | super.destroy();
25 | this._props = {};
26 | }
27 | }
28 |
29 | export default CustomEditorView;
30 |
--------------------------------------------------------------------------------
/src/joinDown.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | // https://github.com/ProseMirror/prosemirror-commands/blob/master/src/commands.js
3 |
4 | import { NodeSelection } from 'prosemirror-state';
5 | import { Transform , canJoin, joinPoint} from 'prosemirror-transform';
6 |
7 | // Join the selected block, or the closest ancestor of the selection
8 | // that can be joined, with the sibling after it.
9 | export default function joinDown(tr: Transform): Transform {
10 | const sel = tr.selection;
11 | let point;
12 | if (sel instanceof NodeSelection) {
13 | if (sel.node.isTextblock || !canJoin(tr.doc, sel.to)) {
14 | return tr;
15 | }
16 | point = sel.to;
17 | } else {
18 | point = joinPoint(tr.doc, sel.to, 1);
19 | if (point === null || point === undefined) {
20 | return tr;
21 | }
22 | }
23 | tr = tr.join(point);
24 | return tr;
25 | }
26 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "allowJs": true,
4 | "jsx": "react",
5 | "target": "ES2022",
6 | "module": "esnext",
7 | "moduleResolution": "node",
8 | "allowSyntheticDefaultImports": true,
9 | "lib": [
10 | "es2018",
11 | "dom",
12 | "dom.Iterable"
13 | ],
14 | "outDir": "dist",
15 | "pretty": false,
16 | "esModuleInterop": true,
17 | "skipLibCheck": true,
18 | "types": [
19 | "jest",
20 | "node",
21 | "jest-prosemirror"
22 | ],
23 | // Ensure that .d.ts files are created by tsc, but not .js files
24 | "declaration": true,
25 | "emitDeclarationOnly": true
26 | },
27 | "typeAcquisition": {
28 | "enable": true
29 | },
30 | "include": [
31 | "src/**/*.ts",
32 | "src/**/*.js"
33 | ],
34 | "exclude": [
35 | "node_modules",
36 | ]
37 | }
--------------------------------------------------------------------------------
/src/patchBreakElements.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | export default function patchBreakElements(doc: Document): void {
4 | // This is a workaround to handle HTML converted from DraftJS that
5 | // `
` becomes `
`.
6 | // Block with single `
` inside should be collapsed into ``.
7 | const selector = 'div > span:only-child > br:only-child';
8 | Array.from(doc.querySelectorAll(selector)).forEach(patchBreakElement);
9 | }
10 |
11 | function patchBreakElement(brElement: HTMLElement): void {
12 | const { ownerDocument, parentElement } = brElement;
13 | if (!ownerDocument || !parentElement) {
14 | return;
15 | }
16 | const div = brElement.parentElement?.parentElement;
17 | if (!div) {
18 | return;
19 | }
20 | const pp = ownerDocument.createElement('p');
21 | div.parentElement?.replaceChild(pp, div);
22 | }
23 |
--------------------------------------------------------------------------------
/src/ui/handleEditorPaste.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | import { EditorView } from 'prosemirror-view';
4 |
5 | import { uploadImageFiles } from '../ImageUploadPlaceholderPlugin.js';
6 |
7 | // workaround to support ClipboardEvent as a valid type.
8 | // https://github.com/facebook/flow/issues/1856
9 | declare class ClipboardEvent extends Event {
10 | clipboardData: DataTransfer;
11 | }
12 |
13 | export default function handleEditorPaste(
14 | view: EditorView,
15 | event: ClipboardEvent
16 | ): boolean {
17 | const { clipboardData } = event;
18 | if (!clipboardData) {
19 | return false;
20 | }
21 |
22 | const { files } = clipboardData;
23 | if (!files?.length) {
24 | return false;
25 | }
26 | const filesList = Array.from(files);
27 |
28 | if (uploadImageFiles(view, filesList)) {
29 | event.preventDefault();
30 | return true;
31 | }
32 | return false;
33 | }
34 |
--------------------------------------------------------------------------------
/src/BlockquoteNodeSpec.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | import { Node } from 'prosemirror-model';
4 |
5 | import ParagraphNodeSpec,{ getParagraphNodeAttrs, toParagraphDOM } from './ParagraphNodeSpec.js';
6 |
7 | import type { NodeSpec } from './Types.js';
8 |
9 | // https://github.com/ProseMirror/prosemirror-schema-basic/blob/master/src/schema-basic.js
10 | // :: NodeSpec A plain paragraph textblock. Represented in the DOM
11 | // as a `` element.
12 | const BlockquoteNodeSpec: NodeSpec = {
13 | ...ParagraphNodeSpec,
14 | defining: true,
15 | parseDOM: [{ tag: 'blockquote', getAttrs }],
16 | toDOM,
17 | };
18 |
19 | function toDOM(node: Node): Array {
20 | const dom = toParagraphDOM(node);
21 | dom[0] = 'blockquote';
22 | return dom;
23 | }
24 |
25 | function getAttrs(dom: HTMLElement): Object {
26 | return getParagraphNodeAttrs(dom);
27 | }
28 |
29 | export default BlockquoteNodeSpec;
30 |
--------------------------------------------------------------------------------
/src/HangingIndentMarkSpec.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | import { Node } from 'prosemirror-model';
4 | import type { MarkSpec } from './Types.js';
5 |
6 | const HangingIndentMarkSpec: MarkSpec = {
7 | attrs: {
8 | prefix: { default: null },
9 | overridden: { default: false },
10 | },
11 | inline: true,
12 | group: 'inline',
13 | parseDOM: [
14 | {
15 | tag: 'span[prefix]',
16 | getAttrs: (domNode) => {
17 | const _prefix = domNode.getAttribute('prefix');
18 | return { prefix: _prefix || null, overridden: true };
19 | },
20 | },
21 | ],
22 | toDOM(node: Node) {
23 | const { prefix } = node.attrs;
24 | const attrs = { prefix, overridden: true };
25 | return ['span', attrs, 0];
26 | },
27 | rank: 5000
28 | };
29 |
30 | export default HangingIndentMarkSpec;
31 |
--------------------------------------------------------------------------------
/src/MarkNames.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | // https://github.com/ProseMirror/prosemirror-schema-basic/blob/master/src/schema-basic.js
4 | export const MARK_CODE = 'code';
5 | export const MARK_EM = 'em';
6 | export const MARK_FONT_SIZE = 'mark-font-size';
7 | export const MARK_FONT_TYPE = 'mark-font-type';
8 | export const MARK_LINK = 'link';
9 | export const MARK_NO_BREAK = 'mark-no-break';
10 | export const MARK_STRIKE = 'strike';
11 | export const MARK_STRONG = 'strong';
12 | export const MARK_SUPER = 'super';
13 | export const MARK_SUB = 'sub';
14 | export const MARK_TEXT_COLOR = 'mark-text-color';
15 | export const MARK_TEXT_HIGHLIGHT = 'mark-text-highlight';
16 | export const MARK_TEXT_SELECTION = 'mark-text-selection';
17 | export const MARK_UNDERLINE = 'underline';
18 | export const MARK_SPACER = 'spacer';
19 | export const MARK_OVERRIDE = 'override';
20 | export const MARK_HANGING_INDENT = 'mark-hanging-indent';
21 |
--------------------------------------------------------------------------------
/src/NodeNames.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | // https://github.com/ProseMirror/prosemirror-schema-basic/blob/master/src/schema-basic.js
4 | export const BLOCKQUOTE = 'blockquote';
5 | export const BOOKMARK = 'bookmark';
6 | export const BULLET_LIST = 'bullet_list';
7 | export const CODE_BLOCK = 'code_block';
8 | export const DOC = 'doc';
9 | export const HARD_BREAK = 'hard_break';
10 | export const HEADING = 'heading';
11 | export const HORIZONTAL_RULE = 'horizontal_rule';
12 | export const IMAGE = 'image';
13 | export const LINK = 'link';
14 | export const LIST_ITEM = 'list_item';
15 | export const MATH = 'math';
16 | export const ORDERED_LIST = 'ordered_list';
17 | export const PARAGRAPH = 'paragraph';
18 | export const TABLE = 'table';
19 | export const TABLE_CELL = 'table_cell';
20 | export const TABLE_HEADER = 'table_header';
21 | export const TABLE_ROW = 'table_row';
22 | export const TEXT = 'text';
23 | export const UNDERLINE = 'underline';
24 |
--------------------------------------------------------------------------------
/style-service.Dockerfile:
--------------------------------------------------------------------------------
1 | # To build:
2 | # docker build . -f style-service.Dockerfile -t style-service:latest
3 |
4 | # To run (simple demo, data is lost when container is removed):
5 | # docker run -d -p 3005:3005 --name style-service style-service
6 |
7 | # To make data persist across containers, create a volume:
8 | # docker volume create style-service-data
9 | # Then bind container to volume when run
10 | # docker run -d -p3005:3005 -v style-service-data:/app/customstyles/ --name style-service style-service
11 |
12 | FROM node:alpine
13 |
14 | RUN mkdir -p /app/customstyles && mkdir -p /app/server && chown -R node:node /app
15 |
16 | USER node:node
17 |
18 | COPY servers/customstyles/run_customstyle_server.bundle.js /app/server/index.js
19 |
20 | # Create volume to save styles if container is stopped.
21 | VOLUME /app/customstyles
22 |
23 | # Expose the server using default port
24 | EXPOSE 3005
25 |
26 | CMD cd /app/server && node .
27 |
--------------------------------------------------------------------------------
/src/ui/czi-table-grid-size-editor.css:
--------------------------------------------------------------------------------
1 | .czi-table-grid-size-editor {
2 | background: #fff;
3 | box-shadow: var(--czi-overlay-shadow);
4 | font-family: var(--czi-font-family);
5 | font-size: var(--czi-font-size);
6 | }
7 |
8 | .czi-table-grid-size-editor-body {
9 | position: relative;
10 | }
11 |
12 | .czi-table-grid-size-editor-body::after {
13 | background: rgba(0, 0, 0, 0);
14 | bottom: -50px;
15 | content: '';
16 | left: 0;
17 | position: absolute;
18 | right: -50px;
19 | top: -50px;
20 | }
21 |
22 | .czi-table-grid-size-editor-cell {
23 | border: var(--czi-border-grey);
24 | box-sizing: border-box;
25 | position: absolute;
26 | z-index: 2;
27 | }
28 |
29 | .czi-table-grid-size-editor-cell.selected {
30 | background: var(--czi-selection-highlight-color);
31 | border-color: var(--czi-selection-highlight-color-dark);
32 | }
33 |
34 | .czi-table-grid-size-editor-footer {
35 | padding-bottom: 5px;
36 | text-align: center;
37 | }
38 |
--------------------------------------------------------------------------------
/src/ui/czi-image-url-editor.css:
--------------------------------------------------------------------------------
1 | @import './czi-vars.css';
2 |
3 | .czi-image-url-editor {
4 | background: #fff;
5 | box-shadow: var(--czi-overlay-shadow);
6 | font-family: var(--czi-font-family);
7 | font-size: var(--czi-font-size);
8 | padding: 4px 10px;
9 | }
10 |
11 | .czi-image-url-editor .czi-form {
12 | max-width: 90vw;
13 | min-width: 40vw;
14 | width: 500px;
15 | }
16 |
17 | .czi-image-url-editor-src-input-row {
18 | position: relative;
19 | }
20 |
21 | .czi-image-url-editor-src-input-row input.czi-image-url-editor-src-input {
22 | padding-right: 40px;
23 | }
24 |
25 | .czi-image-url-editor-input-preview {
26 | background-color: #fff;
27 | background-position: center;
28 | background-repeat: no-repeat;
29 | background-size: contain;
30 | border: solid 1px #fff;
31 | bottom: 1px;
32 | box-sizing: border-box;
33 | outline: var(--czi-border-blue);
34 | position: absolute;
35 | right: 0;
36 | top: 2px;
37 | width: 36px;
38 | }
39 |
--------------------------------------------------------------------------------
/src/HorizontalRuleNodeSpec.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | import { Node } from 'prosemirror-model';
4 |
5 | const DOM_ATTRIBUTE_PAGE_BREAK = 'data-page-break';
6 |
7 | function getAttrs(dom: HTMLElement) {
8 | const attrs = {};
9 | if (
10 | dom.getAttribute(DOM_ATTRIBUTE_PAGE_BREAK) ||
11 | dom.style.pageBreakBefore === 'always'
12 | ) {
13 | // Google Doc exports page break as HTML:
14 | // ` "`
11 | // at the start of a textblock into a blockquote.
12 | const MACRO_PATTERN = /^\s*>\s$/;
13 |
14 | function handleBlockQuoteInputRule(
15 | state: EditorState,
16 | match: any,
17 | start: any,
18 | end: any
19 | ): Transform {
20 | const { schema } = state;
21 | let { tr } = state;
22 | const nodeType = schema.nodes[BLOCKQUOTE];
23 | if (!nodeType) {
24 | return tr;
25 | }
26 |
27 | tr = toggleBlockquote(tr, schema);
28 | if (tr.docChanged) {
29 | tr = tr.delete(start, end);
30 | }
31 | return tr;
32 | }
33 |
34 | export default function blockQuoteInputRule(): InputRule {
35 | return new InputRule(MACRO_PATTERN, handleBlockQuoteInputRule);
36 | }
37 |
--------------------------------------------------------------------------------
/src/BookmarkNodeSpec.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | import type { NodeSpec } from './Types.js';
4 |
5 | export const ATTRIBUTE_BOOKMARK_ID = 'data-bookmark-id';
6 | export const ATTRIBUTE_BOOKMARK_VISIBLE = 'data-bookmark-visible';
7 |
8 | function getAttrs(dom: HTMLElement) {
9 | const id = dom.getAttribute(ATTRIBUTE_BOOKMARK_ID);
10 | const visible = dom.getAttribute(ATTRIBUTE_BOOKMARK_VISIBLE) === 'true';
11 | return {
12 | id,
13 | visible,
14 | };
15 | }
16 |
17 | const BookmarkNodeSpec: NodeSpec = {
18 | inline: true,
19 | attrs: {
20 | id: { default: null },
21 | visible: { default: null },
22 | },
23 | group: 'inline',
24 | draggable: true,
25 | parseDOM: [{ tag: `a[${ATTRIBUTE_BOOKMARK_ID}]`, getAttrs }],
26 | toDOM(node) {
27 | const { id, visible } = node.attrs;
28 | const attrs = id
29 | ? {
30 | [ATTRIBUTE_BOOKMARK_ID]: id,
31 | [ATTRIBUTE_BOOKMARK_VISIBLE]: visible,
32 | id,
33 | }
34 | : {};
35 | return ['a', attrs];
36 | },
37 | };
38 |
39 | export default BookmarkNodeSpec;
40 |
--------------------------------------------------------------------------------
/src/LinkMarkSpec.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | import type { MarkSpec } from './Types.js';
4 |
5 | const LinkMarkSpec: MarkSpec = {
6 | attrs: {
7 | href: { default: null },
8 | rel: { default: 'noopener noreferrer nofollow' },
9 | target: { default: 'blank' },
10 | title: { default: null },
11 | selectionId: {
12 | default: null,
13 | },
14 | },
15 | inclusive: false,
16 | parseDOM: [
17 | {
18 | tag: 'a[href]',
19 | getAttrs: (dom) => {
20 | const href = dom.getAttribute('href');
21 | const target = href?.indexOf('#') === 0 ? '' : 'blank';
22 | const selectionId = dom.getAttribute('selectionId') ?? '';
23 | return {
24 | href: dom.getAttribute('href'),
25 | title: dom.getAttribute('title'),
26 | target,
27 | selectionId,
28 | };
29 | },
30 | },
31 | ],
32 | toDOM(node) {
33 | const attrs = {
34 | ...node.attrs,
35 | onclick: 'return false',
36 | };
37 | return ['a', attrs, 0];
38 | },
39 | };
40 |
41 | export default LinkMarkSpec;
42 |
--------------------------------------------------------------------------------
/.flowconfig:
--------------------------------------------------------------------------------
1 | [include]
2 | .*/src/.*
3 | .*/licit/.*
4 | [ignore]
5 | .*/dist/.*
6 | .*/bin/.*
7 | .*/node_modules/@babel.*
8 | .*/node_modules/@emotion/.*
9 | .*/node_modules/babel-plugin-emotion/.*
10 | .*/node_modules/chalk/.*
11 | .*/node_modules/create-emotion-styled/.*
12 | .*/node_modules/create-emotion/.*
13 | .*/node_modules/create-react-context/.*
14 | .*/node_modules/draft-convert/.*
15 | .*/node_modules/draft-js/.*
16 | .*/node_modules/eslint-plugin-jsx-a11y/.*
17 | .*/node_modules/eslint/.*
18 | .*/node_modules/flow-webpack-plugin/.*
19 | .*/node_modules/inquirer/.*
20 | .*/node_modules/jsondiffpatch/.*
21 | .*/node_modules/katex/.*
22 | .*/node_modules/match-at/.*
23 | .*/node_modules/postcss-modules-extract-imports/.*
24 | .*/node_modules/postcss-modules-local-by-default/.*
25 | .*/node_modules/postcss-modules-scope/.*
26 | .*/node_modules/postcss-modules-values/.*
27 | .*/node_modules/postcss/.*
28 | .*/node_modules/react-emotion/.*
29 | .*/node_modules/unstated/.*
30 | .*/node_modules/stylelint/.*
31 | .*/node_modules/gensync/.*
32 |
33 | [libs]
34 | ./flow-typed/.*
35 |
36 | [options]
37 |
--------------------------------------------------------------------------------
/scripts/webserver.js:
--------------------------------------------------------------------------------
1 | /*eslint-env node*/
2 |
3 | import WebpackDevServer from 'webpack-dev-server';
4 | import webpack from 'webpack';
5 | import config from '../webpack.config';
6 | import env from './env';
7 | import path from 'path';
8 |
9 | const options = config.chromeExtensionBoilerplate || {};
10 | const excludeEntriesToHotReload = options.notHotReload || [];
11 |
12 | for (const entryName in config.entry) {
13 | if (excludeEntriesToHotReload.indexOf(entryName) === -1) {
14 | config.entry[entryName] = [
15 | 'webpack-dev-server/client?http://localhost:' + env.PORT,
16 | 'webpack/hot/dev-server',
17 | ].concat(config.entry[entryName]);
18 | }
19 | }
20 |
21 | config.plugins = [new webpack.HotModuleReplacementPlugin()].concat(
22 | config.plugins || []
23 | );
24 |
25 | delete config.chromeExtensionBoilerplate;
26 |
27 | const compiler = webpack(config);
28 |
29 | const server = new WebpackDevServer(compiler, {
30 | hot: true,
31 | contentBase: path.join(__dirname, '../build'),
32 | headers: {'Access-Control-Allow-Origin': '*'},
33 | });
34 |
35 | server.listen(env.PORT);
36 |
--------------------------------------------------------------------------------
/src/TextColorMarkSpec.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | import { Node } from 'prosemirror-model';
4 |
5 | import toCSSColor from './ui/toCSSColor.js';
6 |
7 | import type { MarkSpec } from './Types.js';
8 |
9 | const TextColorMarkSpec: MarkSpec = {
10 | attrs: {
11 | color: { default: null }, // Allow missing color
12 | overridden: { default: false },
13 | },
14 | inline: true,
15 | group: 'inline',
16 | parseDOM: [
17 | {
18 | tag: 'span[style*=color]',
19 | getAttrs: (dom: HTMLElement) => {
20 | const { color } = dom.style;
21 | const overridden = dom.getAttribute('overridden') === 'true'; // Extract overridden flag
22 | return {
23 | color: toCSSColor(color),
24 | overridden
25 | };
26 | },
27 | },
28 | ],
29 | toDOM(node: Node) {
30 | const { color, overridden } = node.attrs;
31 | const attrs = {};
32 | if (color) {
33 | attrs.style = `color: ${color};`;
34 | attrs['overridden'] = overridden?.toString();
35 | }
36 | return ['span', attrs, 0];
37 | },
38 | };
39 |
40 | export default TextColorMarkSpec;
41 |
--------------------------------------------------------------------------------
/src/convertFromDOMElement.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | import { DOMParser, Schema } from 'prosemirror-model';
4 | import { EditorState, Plugin } from 'prosemirror-state';
5 | import { getAttrs } from './DocNodeSpec.js';
6 | import EditorPlugins from './EditorPlugins.js';
7 | import EditorSchema from './EditorSchema.js';
8 |
9 | export default function convertFromDOMElement(
10 | el: HTMLElement,
11 | schema: Schema,
12 | plugins: Array
13 | ): EditorState {
14 | const effectiveSchema = schema || EditorSchema;
15 | const effectivePlugins = plugins || EditorPlugins;
16 | const bodyEl = el.querySelector('body');
17 |
18 | // https://prosemirror.net/docs/ref/#model.ParseOptions.preserveWhitespace
19 | const doc = DOMParser.fromSchema(effectiveSchema).parse(el, {
20 | preserveWhitespace: true,
21 | });
22 |
23 | if (bodyEl) {
24 | // Unfortunately the root node `doc` does not supoort `parseDOM`, thus
25 | // we'd have to assign its `attrs` manually.
26 | doc.attrs = getAttrs(bodyEl);
27 | }
28 |
29 | return EditorState.create({
30 | doc,
31 | plugins: effectivePlugins,
32 | });
33 | }
34 |
--------------------------------------------------------------------------------
/src/ui/CustomMenuItem.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | import { CustomButton } from '@modusoperandi/licit-ui-commands';
4 | import * as React from 'react';
5 |
6 | class CustomMenuItemSeparator extends React.PureComponent {
7 | render(): React.Element {
8 | return ;
9 | }
10 | }
11 |
12 | class CustomMenuItem extends React.PureComponent {
13 | static Separator = CustomMenuItemSeparator;
14 |
15 | props: {
16 | label: string,
17 | disabled?: ?boolean,
18 | onClick: ?(value: any, e: SyntheticEvent<>) => void,
19 | onMouseEnter: ?(value: any, e: SyntheticEvent<>) => void,
20 | value: any,
21 | };
22 |
23 | render(): React.Element {
24 | // [FS] IRAD-1044 2020-09-22
25 | // Added a new class to adjust the width of the custom style menu dropdown.
26 |
27 | let className = 'czi-custom-menu-item';
28 | if (this.props.value._customStyleName) {
29 | className += ' custom-style-menu-item';
30 | }
31 | return ;
32 | }
33 | }
34 |
35 | export default CustomMenuItem;
36 |
--------------------------------------------------------------------------------
/src/joinUp.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | // https://github.com/ProseMirror/prosemirror-commands/blob/master/src/commands.js
3 |
4 | import { NodeSelection } from 'prosemirror-state';
5 | import { Transform, canJoin, joinPoint } from 'prosemirror-transform';
6 |
7 | // Join the selected block or, if there is a text selection, the
8 | // closest ancestor block of the selection that can be joined, with
9 | // the sibling above it.
10 | export default function joinUp(tr: Transform): Transform {
11 | const sel = tr.selection;
12 | const nodeSel = sel instanceof NodeSelection;
13 | let point;
14 | if (nodeSel) {
15 | if (sel.node.isTextblock || !canJoin(tr.doc, sel.from)) {
16 | return tr;
17 | }
18 | point = sel.from;
19 | } else {
20 | point = joinPoint(tr.doc, sel.from, -1);
21 | if (point === null || point === undefined) {
22 | return tr;
23 | }
24 | }
25 |
26 | tr = tr.join(point);
27 | if (nodeSel) {
28 | tr = tr.setSelection(
29 | NodeSelection.create(
30 | tr.doc,
31 | point - tr.doc.resolve(point).nodeBefore.nodeSize
32 | )
33 | );
34 | }
35 |
36 | return tr;
37 | }
38 |
--------------------------------------------------------------------------------
/src/ui/czi-image-upload-placeholder.css:
--------------------------------------------------------------------------------
1 | .czi-image-upload-placeholder {
2 | background: #fff;
3 | border-radius: 5px;
4 | box-shadow: 0 0 3px rgba(0, 0, 0, 0.28);
5 | display: inline-block;
6 | height: 46px;
7 | margin: 0 2px 0 0;
8 | position: relative;
9 | width: 56px;
10 | }
11 |
12 | .czi-image-upload-placeholder-child {
13 | animation: cziImageUploadPlaceholder 1.2s cubic-bezier(0, 0.5, 0.5, 1)
14 | infinite;
15 | background: #ccc;
16 | border-radius: 3px;
17 | left: 6px;
18 | position: absolute;
19 | width: 5px;
20 | }
21 |
22 | .czi-image-upload-placeholder-child:nth-child(1) {
23 | animation-delay: -0.24s;
24 | left: 10px;
25 | }
26 |
27 | .czi-image-upload-placeholder-child:nth-child(2) {
28 | animation-delay: -0.12s;
29 | left: 24px;
30 | }
31 |
32 | .czi-image-upload-placeholder-child:nth-child(3) {
33 | animation-delay: 0;
34 | left: 38px;
35 | }
36 |
37 | @keyframes cziImageUploadPlaceholder {
38 | 0% {
39 | height: 40px;
40 | opacity: 0.5;
41 | top: 4px;
42 | }
43 |
44 | 50%,
45 | 100% {
46 | height: 26px;
47 | opacity: 1;
48 | top: 10px;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/ui/findActiveFontType.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | import { EditorState } from 'prosemirror-state';
4 |
5 | import { MARK_FONT_TYPE } from '../MarkNames.js';
6 | import findActiveMark from '../findActiveMark.js';
7 |
8 | // This should map to `--czi-content-font-size` at `czi-editor.css`.
9 | export const FONT_TYPE_NAME_DEFAULT = 'Arial';
10 |
11 | export default function findActiveFontType(state: EditorState): string {
12 | const { schema, doc, selection, tr } = state;
13 | const markType = schema.marks[MARK_FONT_TYPE];
14 | if (!markType) {
15 | return FONT_TYPE_NAME_DEFAULT;
16 | }
17 | const { from, to, empty } = selection;
18 |
19 | if (empty) {
20 | const storedMarks =
21 | tr.storedMarks ||
22 | state.storedMarks ||
23 | selection.$cursor?.marks?.() || [];
24 | const sm = storedMarks.find((m) => m.type === markType);
25 | return (sm?.attrs.name) || FONT_TYPE_NAME_DEFAULT;
26 | }
27 |
28 | const mark = findActiveMark(doc, from, to, markType);
29 | const fontName = mark?.attrs.name;
30 | if (!fontName) {
31 | return FONT_TYPE_NAME_DEFAULT;
32 | }
33 |
34 | return fontName;
35 | }
36 |
--------------------------------------------------------------------------------
/src/rebaseDocWithSteps.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | import { Step } from 'prosemirror-transform';
4 | import EditorSchema from './EditorSchema';
5 |
6 | type RebaseResult = {
7 | docJSON: Object,
8 | stepsJSON: Array