Skip to content

OJS integration guide

This guide covers what you need to embed the SciFlow editor in an OJS plugin. It assumes familiarity with OJS plugin development.

1. Load the bundle

npm install @sciflow/editor-start
cp node_modules/@sciflow/editor-start/dist/bundle/sciflow-editor.js vendor/sciflow-editor.js
# or resolve via the package export:
#   import '@sciflow/editor-start/bundle';
$templateMgr->addJavaScript(
    'sciflow-editor',
    $request->getBaseUrl() . '/plugins/generic/myPlugin/vendor/sciflow-editor.js',
    ['type' => 'module', 'priority' => STYLE_SEQUENCE_LATE]
);

Icons are included as inline SVGs, so they do not require external fonts or CDN links.

2. Self-host MathJax

Many institutional OJS hosts enforce CSP policies that block CDN scripts. Self-host MathJax alongside your plugin:

npm install mathjax@4
cp -r node_modules/mathjax/es5 vendor/mathjax/

Register vendor/mathjax/tex-svg.js the same way. Copy the entire es5/ directory — MathJax loads sub-resources dynamically at runtime.

3. Map references

OJS calls the bibliography string rawCitation, while SciFlow calls it rawReference. This value is the complete reference-list entry, not an in-text citation. Both fields may contain basic HTML such as <i>. Map them when loading and saving:

// OJS → SciFlow
const sciflowRef = { id: ojsCitation.id, rawReference: ojsCitation.rawCitation };

// SciFlow → OJS
const ojsCitation = { id: sciflowRef.id, rawCitation: sciflowRef.rawReference };

See the Reference Integration guide for full wiring (events, highlighting, drag-and-drop).

4. Connect figure uploads

Wire your OJS file API into the figure feature. See the Figure File API guide for the full handler interface.

5. Fullscreen

If you offer fullscreen editing, use CSS (position: fixed; inset: 0) instead of the browser's native Fullscreen API. The native API captures Escape, which breaks ProseMirror's selectParentNode and the math editor's cancel action.

6. Light DOM and theme interaction

The ProseMirror editable surface (contenteditable) is a light-DOM child of <sciflow-editor> with the class sf-editable-surface. It is not inside the element's shadow root. This structure has 2 consequences for OJS themes:

  • Your journal theme's global CSS reaches the editor's content — any theme rule targeting .ProseMirror, p, h1, and similar selectors anywhere on the page also applies inside the editor. Check your theme's typography and reset rules against a mounted editor before shipping.
  • To style editor content deliberately (typography, decoration/highlight classes), write global CSS scoped to .sf-editable-surface .ProseMirror. The shadowStyles property and setShadowStyles() method only reach the element's shadow root — they style chrome (border, focus ring, popovers), never the editable content.

See Styling Editor Content (Light DOM) for the full styling model and worked examples.

7. Use custom plugins in OJS

When your OJS plugin loads the bundle described in section 1, import custom features from @sciflow/editor-start/bundle. Do not import them from the bare @sciflow/editor-core package. The /bundle entry includes its own copies of @sciflow/editor-core and prosemirror-view. A feature built against other copies fails at runtime when it registers commands or decorations. See Adding custom ProseMirror plugins for examples, including the createReadOnlyFeature and createRangeDecorationsFeature factories.

8. Save with the editor-change contract

editor-change fires when the document changes or references are updated, not after every transaction. Its operations[] array can contain operations from multiple transactions. A selection-only transaction has 0 steps and does not fire its own event. Its operation remains queued until a later transaction meets the event condition. Do not assume operations.length === 1. If your save loop only handles content edits, filter for operations with steps (op.steps?.length > 0). Treating every queued operation as an edit can cause duplicate saves or incorrect diffs. See Web components basics: Events for the complete contract.

9. Troubleshooting

Symptom Fix
Equations show raw TeX Check browser console for CSP errors; verify the MathJax script path
MathJax sub-resources 404 Copy the full es5/ directory, not just tex-svg.js
sciflow-editor is undefined Ensure type is set to module in addJavaScript
MathJax loads from CDN Search templates for cdn.jsdelivr.net/npm/mathjax and remove

10. Import manuscripts (DOCX, Markdown, and LaTeX)

@sciflow/pandoc-web converts an author's file to a SciFlow document in the browser. The conversion requires neither Pandoc on the OJS server nor an upload to the server. See Importing documents for the complete process. The following steps apply to OJS data.

  • The engine binary. The converter is a 58 MB WebAssembly file. Bundle it with the plugin's assets, or host it and point the converter at it with engineUrl — see Providing the engine yourself. The browser fetches and caches it once.
  • Media go to OJS submission files. The import hands you the embedded images as Blobs with media:<path> placeholders in the document. Upload each one through the same uploadFile handler configured for figures in section 4. Rewrite each src to the returned URL, and add the descriptors to files. See Persisting media and references. Complete this work during import so the first editor-change contains OJS-hosted URLs. Nothing in the saved document points at a blob: address.
  • References go to OJS citations. parseResult.references carries one CSL-JSON record per bibliography entry ({ id, csl, label? }). Store those in your submission's citation data at import time. OJS stores the rendered string as rawCitation, while the editor calls it rawReference. Pass each record to the editor as a SnapshotReference that retains the csl key. Every later editor-change returns them as detail.references, so your save loop and your import path write the same records.
  • Check doc before loading. A conversion can complete with doc: null and errors; the sciflow-document event still fires. See Handling failures.