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:
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. TheshadowStylesproperty andsetShadowStyles()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 withmedia:<path>placeholders in the document. Upload each one through the sameuploadFilehandler configured for figures in section 4. Rewrite eachsrcto the returned URL, and add the descriptors tofiles. See Persisting media and references. Complete this work during import so the firsteditor-changecontains OJS-hosted URLs. Nothing in the saved document points at ablob:address. - References go to OJS citations.
parseResult.referencescarries 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 asrawCitation, while the editor calls itrawReference. Pass each record to the editor as aSnapshotReferencethat retains thecslkey. Every latereditor-changereturns them asdetail.references, so your save loop and your import path write the same records. - Check
docbefore loading. A conversion can complete withdoc: nulland errors; thesciflow-documentevent still fires. See Handling failures.