course-renderer
Version:
Manages CA School Courses file system storage and HTML conversion
163 lines (112 loc) • 4.59 kB
text/typescript
import Reader from '../reader'
const ANNOTATION_MARKER = 0x2F /* / */
export interface AnnotationInterface {
type: string,
answer: string,
readonly?: string,
caption?: string,
id?: string,
init?: string,
files?: string,
raw: string,
choiceType?: string
}
export function annotation (reader: Reader, silent: boolean): any {
let pos = reader.getCurrentLineStartPos()
if ( reader.getCharCode(pos++) !== ANNOTATION_MARKER
|| reader.getCharCode(pos++) !== ANNOTATION_MARKER
|| reader.getCharCode(pos++) !== ANNOTATION_MARKER ) {
return false
}
if ( silent ) return true
// If we're still here, then we need to parse the annotation line and move the position of the reader cursor
const annotationText = reader.getLine(reader.currentLine)
const parsed: AnnotationInterface = {
type: getType(annotationText),
answer: getAnswer(annotationText),
readonly: getReadonly(annotationText),
caption: getCaption(annotationText),
id: getId(annotationText),
init: getInit(annotationText),
files: getFiles(annotationText),
choiceType: getChoiceType(annotationText),
raw: annotationText
}
if (parsed.type == null) {
throw new Error(`Expecting a 'type' on the annotation but found '${annotationText}`)
}
if ( (parsed.type === 'SS' || parsed.type === 'MS' || parsed.type === 'CR') && parsed.id == null ) {
throw new Error(`SS, MS and CR questions must have an ID at annotation ${annotationText}`)
}
if (parsed.type !== 'SS' && parsed.type !== 'MS' && parsed.type !== 'CR' && parsed.type !== 'TI' && parsed.type !== 'REPL') {
throw new Error(`Annotation type may only have the following value 'SS', 'MS', 'CR' and 'TI' but found ${parsed.type} at annotation ${annotationText}`)
}
if (parsed.type !== 'REPL' && parsed.answer == null) {
throw new Error(`Annotation type CR, SS, MS and TI must have an answer specified at annotation ${annotationText}`)
}
if (parsed.type === 'REPL' && parsed.answer != null) {
throw new Error(`Annotation type REPL cannot have an answer annotation at ${annotationText}`)
}
if (parsed.type != 'REPL' && parsed.readonly != null) {
throw new Error(`readonly annotation can only be applied to REPL type at ${annotationText}`)
}
if (parsed.type == 'REPL' && parsed.readonly != null && parsed.readonly != 'true') {
throw new Error(`readonly annotation can only support a 'true' value at ${annotationText}`)
}
if (parsed.type == 'SS' && parsed.answer.toString().split(',').length > 1) {
throw new Error(`Annotation type SS must only have a single answer at ${annotationText}`)
}
if (parsed.choiceType != 'TEXT' && parsed.choiceType != 'HTML') {
throw new Error(`choiceType annotation can only support a 'TEXT' and 'HTML' value at ${annotationText}`)
}
reader.nextLine()
return parsed;
}
function getId(annotation: string): string {
const ID_RE = /id=(\w+-\w+-\w+-\w+-\w+)/
const match = annotation.match(ID_RE)
if (match === null) return null
return match[1]
}
function getType(annotation: string): string {
const TYPE_RE = /type=(\w\w(\w\w)?)/
const match = annotation.match(TYPE_RE)
if (match == null) return null
return match[1]
}
function getAnswer(annotation: string) {
const ANSWER_RE = /answer=\[(.+?)\]/
const match = annotation.match(ANSWER_RE)
if (match == null) return null
return match[1]
}
function getReadonly(annotation: string) {
const READONLY_RE = /readonly=(\w+)/
const match = annotation.match(READONLY_RE)
if (match == null) return null
return match[1]
}
function getInit(annotation: string) {
const INITIALIZATION_RE = /init=\[(.+?)\]/
const match = annotation.match(INITIALIZATION_RE)
if (match == null) return null
return match[1]
}
function getCaption(annotation: string) {
const CAPTION_RE = /caption="(.+)"/
const match = annotation.match(CAPTION_RE)
if (match == null) return null
return match[1]
}
function getFiles(annotation: string) {
const FILES_RE = /filename=\[(.+?)\]/
const match = annotation.match(FILES_RE)
if (match == null) return null
return match[1]
}
function getChoiceType(annotation: string) {
const CHOICE_RE = /choiceType=(\w+)/
const match = annotation.match(CHOICE_RE);
if (match == null) return 'TEXT';
return match[1];
}