diff --git a/docs/release-notes-player.txt b/docs/release-notes-player.txt index 0759f4a9d8b855d10454239a1f2be1204eddefab..41ba69bcb2b9956a97b89fe38f03406fc68eeb1a 100644 --- a/docs/release-notes-player.txt +++ b/docs/release-notes-player.txt @@ -9,6 +9,7 @@ Player - Fix position of virtual keyboard for text areas - Rename marking tag of text to 'aspect-marked' - Restore the state of likert elements when re-entering a unit +- Change saving format of text markers 1.11.0 - For spelling element use the same font properties for input field and button diff --git a/projects/player/src/app/components/element-container/element-container.component.ts b/projects/player/src/app/components/element-container/element-container.component.ts index 7817f3c3ca1929b85087f98ef096272e5f9dc7d5..a94ecc138392ec9b25d351d7ffbc5dfe0dea548b 100644 --- a/projects/player/src/app/components/element-container/element-container.component.ts +++ b/projects/player/src/app/components/element-container/element-container.component.ts @@ -16,7 +16,6 @@ import { import { FormElementComponent } from '../../../../../common/directives/form-element-component.directive'; import { CompoundElementComponent } from '../../../../../common/directives/compound-element.directive'; -import { TextElement } from '../../../../../common/ui-elements/text/text-element'; import { VideoElement } from '../../../../../common/ui-elements/video/video-element'; import { AudioElement } from '../../../../../common/ui-elements/audio/audio-element'; import { ImageElement } from '../../../../../common/ui-elements/image/image-element'; @@ -251,7 +250,8 @@ export class ElementContainerComponent implements OnInit { if (unitStateElementCode && unitStateElementCode.value !== undefined) { switch (elementModel.type) { case 'text': - elementModel.text = unitStateElementCode.value; + elementModel.text = this.markingService + .restoreMarkings(unitStateElementCode.value as string[], this.elementModel.text); break; case 'image': elementModel.magnifierUsed = unitStateElementCode.value; @@ -272,7 +272,7 @@ export class ElementContainerComponent implements OnInit { private initUnitStateValue = (elementModel: UIElement): { id: string, value: InputElementValue } => { switch (elementModel.type) { case 'text': - return { id: elementModel.id, value: (elementModel as TextElement).text }; + return { id: elementModel.id, value: [] }; case 'image': return { id: elementModel.id, value: (elementModel as ImageElement).magnifierUsed }; case 'video': diff --git a/projects/player/src/app/services/marking.service.ts b/projects/player/src/app/services/marking.service.ts index f5da760a7c7b069371fe694870f665911c153e33..dc920a9ce44c9c2e560fe6950cf99e312dd3b049 100644 --- a/projects/player/src/app/services/marking.service.ts +++ b/projects/player/src/app/services/marking.service.ts @@ -21,7 +21,8 @@ export class MarkingService { this.applyRange(range, selection, mode === 'delete', color, markMode); textComponent.elementValueChanged.emit({ id: textComponent.elementModel.id, - values: [textComponent.elementModel.text as string, element.innerHTML] + values: [this.getMarkingData(textComponent.elementModel.text as string), + this.getMarkingData(element.innerHTML)] }); textComponent.elementModel.text = element.innerHTML; } @@ -29,6 +30,58 @@ export class MarkingService { } // nothing to do! } + getMarkingData = (htmlText: string): string[] => { + const markingStartPattern = new RegExp(`<${MarkingService.MARKING_TAG.toLowerCase()} [a-z]+="[\\w- ;:]+">`); + const markingClosingTag = `</${MarkingService.MARKING_TAG.toLowerCase()}>`; + let newHtmlText = htmlText; + const markCollection = []; + let matchesArray; + do { + matchesArray = newHtmlText.match(markingStartPattern); + if (matchesArray) { + const startMatch = matchesArray[0]; + matchesArray = newHtmlText.match(markingClosingTag); + if (matchesArray) { + const endMatch = matchesArray[0]; + const startIndex = newHtmlText.search(startMatch); + newHtmlText = newHtmlText.replace(startMatch, ''); + const endIndex = newHtmlText.search(endMatch); + newHtmlText = newHtmlText.replace(endMatch, ''); + markCollection.push(`${startIndex}-${endIndex}-${this.getMarkingColor(startMatch)}`); + } + } + } while (matchesArray); + return markCollection; + }; + + restoreMarkings(markings: string[], htmlText: string): string { + let newHtmlText = htmlText; + if (markings.length) { + const markCollectionReversed = [...markings].reverse(); + const markingDataPattern = /^(\d+)-(\d+)-(.+)$/; + const markingClosingTag = `</${MarkingService.MARKING_TAG.toLowerCase()}>`; + markCollectionReversed.forEach(markingData => { + const matchesArray = markingData.match(markingDataPattern); + if (matchesArray) { + const startIndex = Number(matchesArray[1]); + const endIndex = Number(matchesArray[2]); + const startMatch = this.createMarkingStartTag(matchesArray[3]); + newHtmlText = newHtmlText.substring(0, endIndex) + markingClosingTag + newHtmlText.substr(endIndex); + newHtmlText = newHtmlText.substring(0, startIndex) + startMatch + newHtmlText.substr(startIndex); + } + }); + } + return newHtmlText; + } + + private getMarkingColor = (tag: string): string => { + const colors = tag.match(/(?!.*:)\w*(?=;)/); + return (colors) ? colors[0] : 'none'; + }; + + private createMarkingStartTag = + (color: string): string => `<${MarkingService.MARKING_TAG.toLowerCase()} style="background-color: ${color};">`; + private isDescendantOf(node: Node | null, element: HTMLElement): boolean { if (!node || node === document) { return false; @@ -97,13 +150,11 @@ export class MarkingService { if (previousText) { const prev = this.createMarkedElement(color, markMode); prev.append(document.createTextNode(previousText)); - prev.style.textDecoration = `solid underline ${color}`; parentNode?.insertBefore(prev, textElement); } if (nextText) { const end = this.createMarkedElement(color, markMode); end.append(document.createTextNode(nextText)); - end.style.textDecoration = `solid underline ${color}`; parentNode?.insertBefore(end, textElement.nextSibling); } } diff --git a/projects/player/src/index.html b/projects/player/src/index.html index d8f3cf9227d1df70d46def0b05053fa93a8ebb8e..d1998118e580a50ccebf812b564c989afd037833 100644 --- a/projects/player/src/index.html +++ b/projects/player/src/index.html @@ -64,6 +64,7 @@ <option value="eager">eager</option> <option value="on-demand">on-demand</option> </select> +<!-- <button onclick='takeUnitStateSnapshot();'>Take Snapshot</button>--> </div> </body> @@ -91,7 +92,7 @@ sessionId: 'dev', unitDefinition: e.target.result, unitState: { - dataParts: {} + dataParts: unitStateSnapshot }, playerConfig: { stateReportPolicy: document.getElementById('stateReportPolicy').value, @@ -111,5 +112,18 @@ fileReader.readAsText(input['files'][0]); } } + + let unitStateData = {}; + window.addEventListener("message", (event) => { + if (event.data.type === 'vopStateChangedNotification') { + unitStateData = Object.assign(unitStateData, event.data.unitState?.dataParts); + } + console.log('dev-player: unitStateData', unitStateData); + }); + + let unitStateSnapshot = {}; + takeUnitStateSnapshot = () => { + unitStateSnapshot = unitStateData; + }; </script> </html>