-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
EMMR-111 Add transcription marginalia CKE5 plugin
- Loading branch information
1 parent
ba30b42
commit 886e8a8
Showing
11 changed files
with
355 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
9 changes: 9 additions & 0 deletions
9
custom/modules/emmr_transcribe/js/ckeditor5_plugins/transMargin/README.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
This plugin is largely based on CKEditor 5's [block plugin widget tutorial](https://ckeditor.com/docs/ckeditor5/latest/framework/guides/tutorials/implementing-a-block-widget.html), | ||
but with added documentation to facilitate better understanding of CKEditor 5 | ||
plugin development and other minor changes. | ||
|
||
Within `/src` are the multiple files that will be used by the build process to | ||
become a CKEditor 5 plugin in `/build`. Technically, everything in these files | ||
could be in a single `index.js` - the only file the MUST be present is | ||
`/src/index.js`. However, splitting the plugin into concern-specific files has | ||
maintainability benefits. |
14 changes: 14 additions & 0 deletions
14
custom/modules/emmr_transcribe/js/ckeditor5_plugins/transMargin/src/index.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
/** | ||
* @file The build process always expects an index.js file. Anything exported | ||
* here will be recognized by CKEditor 5 as an available plugin. Multiple | ||
* plugins can be exported in this one file. | ||
* | ||
* I.e. this file's purpose is to make plugin(s) discoverable. | ||
*/ | ||
// cSpell:ignore transmargin | ||
|
||
import TransMargin from './transmargin'; | ||
|
||
export default { | ||
TransMargin, | ||
}; |
55 changes: 55 additions & 0 deletions
55
.../modules/emmr_transcribe/js/ckeditor5_plugins/transMargin/src/inserttransmargincommand.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
/** | ||
* @file defines InsertTransMarginCommand, which is executed when the Transcription Marginalia | ||
* toolbar button is pressed. | ||
*/ | ||
// cSpell:ignore transmarginediting | ||
|
||
import { Command } from 'ckeditor5/src/core'; | ||
|
||
export default class InsertTransMarginCommand extends Command { | ||
execute() { | ||
const { model } = this.editor; | ||
|
||
model.change((writer) => { | ||
// Insert <trxnmar>*</trxnmar> at the current selection position | ||
// in a way that will result in creating a valid model structure. | ||
model.insertContent(createTransMargin(writer)); | ||
}); | ||
} | ||
|
||
refresh() { | ||
const { model } = this.editor; | ||
const { selection } = model.document; | ||
|
||
// Determine if the cursor (selection) is in a position where adding a | ||
// transMargin is permitted. This is based on the schema of the model(s) | ||
// currently containing the cursor. | ||
const allowedIn = model.schema.findAllowedParent( | ||
selection.getFirstPosition(), | ||
'transMargin', | ||
); | ||
|
||
// If the cursor is not in a location where a transMargin can be added, return | ||
// null so the addition doesn't happen. | ||
this.isEnabled = allowedIn !== null; | ||
} | ||
} | ||
|
||
function createTransMargin(writer) { | ||
// Create instances of the three elements registered with the editor in | ||
// transmarginediting.js. | ||
const transMargin = writer.createElement('transMargin'); | ||
const transMarginNumber = writer.createElement('transMarginNumber'); | ||
writer.appendText('X', {}, transMarginNumber); | ||
const transMarginText = writer.createElement('transMarginText'); | ||
let marginaliaText = prompt("Enter transcription marginalia text"); | ||
writer.appendText(marginaliaText, {}, transMarginText); | ||
|
||
// Append the title and description elements to the transMargin, which matches | ||
// the parent/child relationship as defined in their schemas. | ||
writer.append(transMarginNumber, transMargin); | ||
writer.append(transMarginText, transMargin); | ||
|
||
// Return the element to be added to the editor. | ||
return transMargin; | ||
} |
24 changes: 24 additions & 0 deletions
24
custom/modules/emmr_transcribe/js/ckeditor5_plugins/transMargin/src/transmargin.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
/** | ||
* @file This is what CKEditor refers to as a master (glue) plugin. Its role is | ||
* just to load the “editing” and “UI” components of this Plugin. Those | ||
* components could be included in this file, but | ||
* | ||
* I.e, this file's purpose is to integrate all the separate parts of the plugin | ||
* before it's made discoverable via index.js. | ||
*/ | ||
// cSpell:ignore transmarginediting transmarginui | ||
|
||
// The contents of TransMarginUI and TransMargin editing could be included in this | ||
// file, but it is recommended to separate these concerns in different files. | ||
import TransMarginEditing from './transmarginediting'; | ||
import TransMarginUI from './transmarginui'; | ||
import { Plugin } from 'ckeditor5/src/core'; | ||
|
||
export default class TransMargin extends Plugin { | ||
// Note that TransMarginEditing and TransMarginUI also extend `Plugin`, but these | ||
// are not seen as individual plugins by CKEditor 5. CKEditor 5 will only | ||
// discover the plugins explicitly exported in index.js. | ||
static get requires() { | ||
return [TransMarginEditing, TransMarginUI]; | ||
} | ||
} |
207 changes: 207 additions & 0 deletions
207
custom/modules/emmr_transcribe/js/ckeditor5_plugins/transMargin/src/transmarginediting.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,207 @@ | ||
import { Plugin } from 'ckeditor5/src/core'; | ||
import { toWidget, toWidgetEditable } from 'ckeditor5/src/widget'; | ||
import { Widget } from 'ckeditor5/src/widget'; | ||
import InsertTransMarginCommand from './inserttransmargincommand'; | ||
|
||
// cSpell:ignore transmargin inserttransmargincommand | ||
|
||
/** | ||
* CKEditor 5 plugins do not work directly with the DOM. They are defined as | ||
* plugin-specific data models that are then converted to markup that | ||
* is inserted in the DOM. | ||
* | ||
* CKEditor 5 internally interacts with transMargin as this model: | ||
* <transMargin> | ||
* <transMarginNumber></transMarginNumber> | ||
* <transMarginText></transMarginText> | ||
* </transMargin> | ||
* | ||
* Which is converted for the browser/user as this markup | ||
* <trxnmar> | ||
* <span class="trxn-number"></span> | ||
* <span class="trxn-text"></span> | ||
* </trxnmar> | ||
* | ||
* This file has the logic for defining the transMargin model, and for how it is | ||
* converted to standard DOM markup. | ||
*/ | ||
export default class TransMarginEditing extends Plugin { | ||
static get requires() { | ||
return [Widget]; | ||
} | ||
|
||
init() { | ||
this._defineSchema(); | ||
this._defineConverters(); | ||
this.editor.commands.add( | ||
'insertTransMargin', | ||
new InsertTransMarginCommand(this.editor), | ||
); | ||
} | ||
|
||
/* | ||
* This registers the structure that will be seen by CKEditor 5 as | ||
* <transMargin> | ||
* <transMarginNumber></transMarginNumber> | ||
* <transMarginText></transMarginText> | ||
* </transMargin> | ||
* | ||
* The logic in _defineConverters() will determine how this is converted to | ||
* markup. | ||
*/ | ||
_defineSchema() { | ||
// Schemas are registered via the central `editor` object. | ||
const schema = this.editor.model.schema; | ||
|
||
schema.register('transMargin', { | ||
// Behaves like a self-contained object (e.g. an image). | ||
isObject: true, | ||
// Allow in places where other blocks are allowed (e.g. directly in the root). | ||
allowWhere: '$block', | ||
}); | ||
|
||
schema.register('transMarginNumber', { | ||
// This creates a boundary for external actions such as clicking and | ||
// and keypress. For example, when the cursor is inside this box, the | ||
// keyboard shortcut for "select all" will be limited to the contents of | ||
// the box. | ||
isLimit: true, | ||
// This is only to be used within transMargin. | ||
allowIn: 'transMargin', | ||
// Allow content that is allowed in blocks (e.g. text with attributes). | ||
allowContentOf: '$block', | ||
}); | ||
|
||
schema.register('transMarginText', { | ||
isLimit: true, | ||
allowIn: 'transMargin', | ||
allowContentOf: '$block', | ||
}); | ||
|
||
schema.addChildCheck((context, childDefinition) => { | ||
// Disallow transMargin inside self or any children. | ||
if ( | ||
context.startsWith('trans') && | ||
childDefinition.name === 'transMargin' | ||
) { | ||
return false; | ||
} | ||
}); | ||
} | ||
|
||
/** | ||
* Converters determine how CKEditor 5 models are converted into markup and | ||
* vice-versa. | ||
*/ | ||
_defineConverters() { | ||
// Converters are registered via the central editor object. | ||
const { conversion } = this.editor; | ||
|
||
// Upcast Converters: determine how existing HTML is interpreted by the | ||
// editor. These trigger when an editor instance loads. | ||
// | ||
// If <trxnmar> is present in the existing markup | ||
// processed by CKEditor, then CKEditor recognizes and loads it as a | ||
// <transMargin> model. | ||
conversion.for('upcast').elementToElement({ | ||
model: 'transMargin', | ||
view: { | ||
name: 'trxnmar', | ||
}, | ||
}); | ||
|
||
// If <span class="trxn-number"> is present in the existing markup | ||
// processed by CKEditor, then CKEditor recognizes and loads it as a | ||
// <transMarginNumber> model, provided it is a child element of <transMargin>, | ||
// as required by the schema. | ||
conversion.for('upcast').elementToElement({ | ||
model: 'transMarginNumber', | ||
view: { | ||
name: 'span', | ||
classes: 'trxn-number', | ||
}, | ||
}); | ||
|
||
// If <span class="trxn-text"> is present in the existing markup | ||
// processed by CKEditor, then CKEditor recognizes and loads it as a | ||
// <transMarginText> model, provided it is a child element of | ||
// <transMargin>, as required by the schema. | ||
conversion.for('upcast').elementToElement({ | ||
model: 'transMarginText', | ||
view: { | ||
name: 'span', | ||
classes: 'trxn-text', | ||
}, | ||
}); | ||
|
||
// Data Downcast Converters: converts stored model data into HTML. | ||
// These trigger when content is saved. | ||
// | ||
// Instances of <transMargin> are saved as | ||
// <trxnmar>{{inner content}}</trxnmar>. | ||
conversion.for('dataDowncast').elementToElement({ | ||
model: 'transMargin', | ||
view: { | ||
name: 'trxnmar', | ||
}, | ||
}); | ||
|
||
// Instances of <transMarginNumber> are saved as | ||
// <span class="trxn-number">{{inner content}}</span>. | ||
conversion.for('dataDowncast').elementToElement({ | ||
model: 'transMarginNumber', | ||
view: { | ||
name: 'span', | ||
classes: 'trxn-number', | ||
}, | ||
}); | ||
|
||
// Instances of <transMarginText> are saved as | ||
// <span class="trxn-text">{{inner content}}</span>. | ||
conversion.for('dataDowncast').elementToElement({ | ||
model: 'transMarginText', | ||
view: { | ||
name: 'span', | ||
classes: 'trxn-text', | ||
}, | ||
}); | ||
|
||
// Editing Downcast Converters. These render the content to the user for | ||
// editing, i.e. this determines what gets seen in the editor. These trigger | ||
// after the Data Upcast Converters, and are re-triggered any time there | ||
// are changes to any of the models' properties. | ||
// | ||
// Convert the <transMargin> model into a container widget in the editor UI. | ||
conversion.for('editingDowncast').elementToElement({ | ||
model: 'transMargin', | ||
view: (modelElement, { writer: viewWriter }) => { | ||
const trxnmar = viewWriter.createContainerElement('trxnmar', {}); | ||
|
||
return toWidget(trxnmar, viewWriter, { label: 'Transcription marginalia widget' }); | ||
}, | ||
}); | ||
|
||
// Convert the <transMarginNumber> model into an editable <span> widget. | ||
conversion.for('editingDowncast').elementToElement({ | ||
model: 'transMarginNumber', | ||
view: (modelElement, { writer: viewWriter }) => { | ||
const span = viewWriter.createContainerElement('span', | ||
{ | ||
class: 'trxn-number' | ||
}); | ||
return toWidget(span, viewWriter); | ||
}, | ||
}); | ||
|
||
// Convert the <transMarginText> model into an editable <span> widget. | ||
conversion.for('editingDowncast').elementToElement({ | ||
model: 'transMarginText', | ||
view: (modelElement, { writer: viewWriter }) => { | ||
const span = viewWriter.createEditableElement('span', { | ||
class: 'trxn-text', | ||
}); | ||
return toWidgetEditable(span, viewWriter); | ||
}, | ||
}); | ||
} | ||
} |
36 changes: 36 additions & 0 deletions
36
custom/modules/emmr_transcribe/js/ckeditor5_plugins/transMargin/src/transmarginui.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
/** | ||
* @file registers the transMargin toolbar button and binds functionality to it. | ||
*/ | ||
|
||
import { Plugin } from 'ckeditor5/src/core'; | ||
import { ButtonView } from 'ckeditor5/src/ui'; | ||
import icon from '../../../../icons/transMargin.svg'; | ||
|
||
export default class TransMarginUI extends Plugin { | ||
init() { | ||
const editor = this.editor; | ||
|
||
// This will register the transMargin toolbar button. | ||
editor.ui.componentFactory.add('transMargin', (locale) => { | ||
const command = editor.commands.get('insertTransMargin'); | ||
const buttonView = new ButtonView(locale); | ||
|
||
// Create the toolbar button. | ||
buttonView.set({ | ||
label: editor.t('Transcription Marginalia'), | ||
icon, | ||
tooltip: true, | ||
}); | ||
|
||
// Bind the state of the button to the command. | ||
buttonView.bind('isOn', 'isEnabled').to(command, 'value', 'isEnabled'); | ||
|
||
// Execute the command when the button is clicked (executed). | ||
this.listenTo(buttonView, 'execute', () => | ||
editor.execute('insertTransMargin'), | ||
); | ||
|
||
return buttonView; | ||
}); | ||
} | ||
} |