Skip to content

Commit

Permalink
Added SSML editor extension for authoring.
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelsharman committed Mar 14, 2024
1 parent f2540a4 commit 96625de
Show file tree
Hide file tree
Showing 39 changed files with 1,778 additions and 42 deletions.
2 changes: 1 addition & 1 deletion docs/index.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/module-Assessment_Activity.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/module-Assessment_App.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/module-Assessment_Diagnostics.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/module-Assessment_Items.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/module-Assessment_Player.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/module-Assessment_Questions.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/module-Assessment_Sections.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/module-Authoring_App.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/module-Authoring_Diagnostics.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/module-Authoring_Navigation.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/module-Authoring_Widgets.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/module-Extensions_Assessment_ariaCountOnNav.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/module-Extensions_Assessment_blockGrammarChecks.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/module-Extensions_Assessment_columnResizer.html

Large diffs are not rendered by default.

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/module-Extensions_Assessment_hideAlternatives.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/module-Extensions_Assessment_keyboardShortcuts.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/module-Extensions_Assessment_magnifier.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/module-Extensions_Assessment_mcqLabelPrefix.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/module-Extensions_Assessment_pageOverlay.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/module-Extensions_Assessment_renderPDF.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/module-Extensions_Assessment_resetResponse.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/module-Extensions_Assessment_toggleTimer.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/module-Extensions_Assessment_whiteNoise.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/module-Extensions_Authoring_contentTabs.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/module-Extensions_Authoring_essayMaxLength.html

Large diffs are not rendered by default.

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/module-Extensions_Authoring_renderPDF.html

Large diffs are not rendered by default.

498 changes: 498 additions & 0 deletions docs/module-Extensions_Authoring_ssmlEditor.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/module-Utils_Logger.html

Large diffs are not rendered by default.

989 changes: 978 additions & 11 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@
"homepage": "https://michaelsharman.github.io/LT/",
"devDependencies": {
"copy-webpack-plugin": "^12.0.2",
"css-loader": "^6.10.0",
"docdash": "^2.0.2",
"jest": "^29.6.4",
"style-loader": "^3.3.4",
"svg-inline-loader": "^0.8.2",
"webpack": "^5.59.1",
"webpack-cli": "^4.9.1"
Expand All @@ -40,6 +42,7 @@
"howler": "^2.2.4",
"mousetrap": "^1.6.5",
"platform-detect": "^3.0.1",
"shuffle-seed": "^1.1.6"
"shuffle-seed": "^1.1.6",
"ssml-editor": "github:michaelsharman/ssml-editor"
}
}
Binary file added src/assets/images/ssmleditor.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
261 changes: 261 additions & 0 deletions src/authoring/extensions/ui/ssmlEditor/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
import * as ssmlEditor from 'ssml-editor/src/index';

/**
* Extensions add specific functionality to Learnosity APIs.
* They rely on modules within LT being available.
*
* --
*
* Adds an SSML editor which can be launched from a custom
* Question Editor button in the toolbar.
*
* You MUST use the object as defined below. Also, note the `toolbar_settings`
* which is required to place the custom button(s) where you want in the toolbar.
*
* ```
* {
* "config": {
* "dependencies": {
* "question_editor_api": {
* "init_options": {
* "rich_text_editor": {
* "customButtons": [
* {
* "func": "launchSsmlEditor",
* "icon": "/path/to/images/text-to-speech.svg",
* "label": "Add SSML",
* "name": "addSsml",
* "attributes": ["content","stimulus","template","options"]
* }
* ],
* "toolbar_settings": {
* "ltr_toolbar": [
* {
* "items": ["Bold","Italic","Underline","-","TextColor","-", "LrnUnderlinedIndicator","-","RemoveFormat","FontSize"],
* "name": "basicstyles"
* },
* {
* "items": ["NumberedList","BulletedList","-","Indent","Outdent"],
* "name": "list"
* },
* {
* "items": ["JustifyLeft","JustifyCenter","JustifyRight","JustifyBlock"],
* "name": "justify"
* },
* {
* "items": ["Link","Unlink"],
* "name": "link"
* },
* {
* "items": ["Image","LrnMath","Table","Blockquote","SpecialChar"],
* "name": "insert"
* },
* {
* "items": ["LrnSimpleFeature"],
* "name": "simplefeature"
* },
* {
* "items": ["LrnResource"],
* "name": "resource"
* },
* {
* "items": ["LrnEditAriaLabel","LrnPopupContent"],
* "name": "editAriaLabel"
* },
* {
* "name": "custombuttons"
* },
* {
* "items": ["Undo","Redo"],
* "name": "clipboard"
* },
* {
* "items": ["Styles"],
* "name": "style"
* },
* {
* "items": ["Sourcedialog"],
* "name": "mode"
* },
* {
* "items": ["lrn_datatable"],
* "name": "data"
* }
* ]
* }
* }
* }
* }
* }
* }
* }
* ```
*
* <p><img src="https://raw.githubusercontent.com/michaelsharman/LT/main/src/assets/images/ssmleditor.png" alt="" width="660"></p>
* @module Extensions/Authoring/ssmlEditor
*/

const state = {
renderedCss: false,
};

/**
* Extension constructor.
* @example
* import { LT } from '@caspingus/lt/src/authoring/index';
*
* LT.init(authorApp); // Set up LT with the Author API application instance variable
* LT.extensions.ssmlEditor.run();
* @since 2.8.0
*/
export function run() {
if (!state.renderedCss) injectCSS();
}

/**
* Called via a custom button in the rich text toolbar.
* Renders a modal with an SSML editor.
* @param {string} attribute Which Question Editor attribute is being edited
* @param {*} callback Called to return to the parent rich-text editor
* @since 2.8.0
*/
export function launchSsmlEditor(attribute, callback) {
const currentSelectedText = getSelection().toString();
const templateSsmlEditor = `
<div class="lrn-qe lrn-qe-modal" style="display: block;" id="lt__ssmlModalWrapper">
<div class="lrn-qe-ui">
<div class="lrn-qe-modal-dialog">
<div class="lrn-qe-modal-dialog-inner">
<div class="lrn-qe-modal-header">
<div class="lrn-qe-form-label lrn-qe-h4 lrn-qe-section-header">
<h4 class="lrn-qe-heading"><label class="lrn-qe-label lrn-qe-form-label-name">Enter SSML</label></h4>
</div>
<button type="button" class="lrn-qe-btn lrn-qe-modal-btn-close" aria-label="Close" tabindex="0">
<span class="lrn-qe-sr-only">Close</span>
<span aria-role="presentation" class="lrn-qe-i-cross"></span>
</button>
</div>
<div data-lrn-qe-selector="modal-outlet">
<div class="lrn-qe-modal-content" data-lrn-qe-modal-section="content">
<div id="editor"></div>
<div id="ssmlStatus"></div>
</div>
<div class="lrn-qe-modal-footer">
<ul class="lrn-qe-ul">
<li class="lrn-qe-li lrn-qe-modal-footer-item">
<button type="button" class="lrn-qe-btn lrn-qe-btn-default"><span>Cancel</span></button>
</li>&nbsp;
<li class="lrn-qe-li lrn-qe-modal-footer-item">
<button type="button" class="lrn-qe-btn lrn-qe-btn-primary lt__ssml-add" data-lrn-qe-modal-action="confirm"><span>Add SSML</span></button>
</li>&nbsp;
<li class="lrn-qe-li lrn-qe-modal-footer-item">
<button type="button" class="lrn-qe-btn lrn-qe-btn-primary lt__ssml-generate-audio"><span>Generate audio</span></button>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
`;

document.querySelector('.learnosity-question-editor').insertAdjacentHTML('beforeEnd', templateSsmlEditor);

let elClose = [];
elClose.push(document.querySelector('#lt__ssmlModalWrapper .lrn-qe-btn-default'));
elClose.push(document.querySelector('#lt__ssmlModalWrapper .lrn-qe-modal-btn-close'));
for (let i = 0; i < elClose.length; i++) {
elClose[i].addEventListener('click', () => {
removeElement('lt__ssmlModalWrapper');
delete window.quill;
return callback(currentSelectedText);
});
}

const elAdd = document.querySelector('#lt__ssmlModalWrapper .lt__ssml-add');
elAdd.addEventListener('click', () => {
removeElement('lt__ssmlModalWrapper');
const ssml = window.quill.getText();
delete window.quill;
return callback(`<span>${ssml}</span>`);
});

if (!window.hasOwnProperty('quill')) {
const quill = ssmlEditor.run('editor');
if (currentSelectedText) {
const cursorLocation = quill.getSelection();
quill.insertText(cursorLocation.index, currentSelectedText);
}
window.quill = quill;
window.ssmlEditor = ssmlEditor;
}
}

/**
* Removes an element from the DOM
* @param {string} id Id of the element to remove from the DOM
* @since 2.8.0
* @ignore
*/
function removeElement(id) {
document.getElementById(id).remove();
}

/**
* Injects the necessary CSS to the header
* @since 2.8.0
* @ignore
*/
function injectCSS() {
const elStyle = document.createElement('style');
const css = `
/* Learnosity SSML TTS styles */
#lt__ssmlModalWrapper .lrn-qe-modal-content {
height: 23em;
}
#lt__ssmlModalWrapper .ql-editor {
height: 12rem;
}
#lt__ssmlModalWrapper .ql-editor p {
margin: 0;
}
#lt__ssmlModalWrapper .ql-toolbar.ql-snow .ql-formats {
margin-right: 14px;
}
.hidden {
display: none;
}
#lt__ssmlModalWrapper .material-symbols-outlined {
font-size: 1.3rem;
position: relative;
top: -2px;
}
#lt__ssmlModalWrapper .ql-picker {
padding: 3px 0;
}
#lt__ssmlModalWrapper .ql-picker-label {
width: 45px;
height: 18px;
}
#ssmlStatus .material-symbols-outlined {
vertical-align: middle;
}
.ssmlStatusValid {
padding: 1em;
background-color: #cbf5d3;
}
.ssmlStatusInvalid {
padding: 1em;
background-color: #ffb9b9;
}
speak s {
text-decoration: none;
}
`;

elStyle.textContent = css;
document.head.append(elStyle);

state.renderedCss = true;
}
2 changes: 2 additions & 0 deletions src/authoring/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ import * as contentTabs from './extensions/ui/contentTabs/index';
import * as languageTextDirection from './extensions/ui/languageTextDirection/index';
import * as renderPDF from './extensions/ui/renderPDF/index';
import * as essayMaxLength from './extensions/validation/essayMaxLength/index';
import * as ssmlEditor from './extensions/ui/ssmlEditor/index';

const extensions = {
extensions: {
contentTabs: { ...contentTabs },
essayMaxLength: { ...essayMaxLength },
languageTextDirection: { ...languageTextDirection },
renderPDF: { ...renderPDF },
ssmlEditor: { ...ssmlEditor },
},
};

Expand Down
4 changes: 4 additions & 0 deletions webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ module.exports = {
test: /\.svg/,
type: 'asset/resource',
},
{
test: /\.css$/i,
use: ['style-loader', 'css-loader'],
},
],
},
};

0 comments on commit 96625de

Please sign in to comment.