import { IXmlElement } from "./../Common/FileIO/Xml" ; import { VexFlowMusicSheetCalculator } from "./../MusicalScore/Graphical/VexFlow/VexFlowMusicSheetCalculator" ; import { VexFlowBackend } from "./../MusicalScore/Graphical/VexFlow/VexFlowBackend" ; import { MusicSheetReader } from "./../MusicalScore/ScoreIO/MusicSheetReader" ; import { GraphicalMusicSheet } from "./../MusicalScore/Graphical/GraphicalMusicSheet" ; import { MusicSheetCalculator } from "./../MusicalScore/Graphical/MusicSheetCalculator" ; import { VexFlowMusicSheetDrawer } from "./../MusicalScore/Graphical/VexFlow/VexFlowMusicSheetDrawer" ; import { SvgVexFlowBackend } from "./../MusicalScore/Graphical/VexFlow/SvgVexFlowBackend" ; import { CanvasVexFlowBackend } from "./../MusicalScore/Graphical/VexFlow/CanvasVexFlowBackend" ; import { MusicSheet } from "./../MusicalScore/MusicSheet" ; import { Cursor } from "./Cursor" ; import { MXLHelper } from "../Common/FileIO/Mxl" ; import { AJAX } from "./AJAX" ; import log from "loglevel" ; import { DrawingParametersEnum , DrawingParameters , ColoringModes } from "../MusicalScore/Graphical/DrawingParameters" ; import { IOSMDOptions , OSMDOptions , AutoBeamOptions , BackendType , CursorOptions } from "./OSMDOptions" ; import { EngravingRules , PageFormat } from "../MusicalScore/Graphical/EngravingRules" ; import { AbstractExpression } from "../MusicalScore/VoiceData/Expressions/AbstractExpression" ; import { Dictionary } from "typescript-collections" ; import { AutoColorSet } from "../MusicalScore/Graphical/DrawingEnums" ; import { GraphicalMusicPage } from "../MusicalScore/Graphical/GraphicalMusicPage" ; import { MusicPartManagerIterator } from "../MusicalScore/MusicParts/MusicPartManagerIterator" ; import { ITransposeCalculator } from "../MusicalScore/Interfaces/ITransposeCalculator" ; import { NoteEnum } from "../Common/DataObjects/Pitch" ; /**
 * The main class and control point of OpenSheetMusicDisplay.<br>
 * It can display MusicXML sheet music files in an HTML element container.<br>
 * After the constructor, use load() and render() to load and render a MusicXML file.
 */ export class OpenSheetMusicDisplay { protected version : string = "1.4.0-dev" ; param container param options constructor ( container , options = OSMDOptions . OSMDOptionsStandard ( ) ) { if ( typeof container === "string" ) { this . container = document . getElementById ( < string > container ) ; } else if ( container && "appendChild" in < any > container ) { this . container = < HTMLElement > container ; } if ( ! this . container ) { throw new Error ( "Please pass a valid div container to OpenSheetMusicDisplay" ) ; } if ( options . autoResize === undefined ) { options . autoResize = true ; } this . backendType = BackendType . SVG ; this . setOptions ( options ) ; } private cursorsOptions : CursorOptions [ ] = [ ] ; public cursors : Cursor [ ] = [ ] ; public get cursor ( ) : Cursor { return this . cursors [ 0 ] ; } public zoom : number = 1.0 ; protected zoomUpdated : boolean = false ; /** Timeout in milliseconds used in osmd.load(string) when string is a URL. */ public loadUrlTimeout : number = 5000 ; protected container : HTMLElement ; protected backendType : BackendType ; protected needBackendUpdate : boolean ; protected sheet : MusicSheet ; protected drawer : VexFlowMusicSheetDrawer ; protected drawBoundingBox : string ; protected drawSkyLine : boolean ; protected drawBottomLine : boolean ; protected graphic : GraphicalMusicSheet ; protected drawingParameters : DrawingParameters ; protected rules : EngravingRules ; protected autoResizeEnabled : boolean ; protected resizeHandlerAttached : boolean ; protected followCursor : boolean ; protected OnXMLRead : Function ; param content public load ( content ) { this . reset ( ) ; if ( typeof content === "string" ) { const str = < string > content ; const self = this ; if ( str . substr ( 0 , 4 ) === "\x50\x4b\x03\x04" ) { log . debug ( "[OSMD] This is a zip file, unpack it first: " + str ) ; return MXLHelper . MXLtoXMLstring ( str ) . then ( ( x ) => { return self . load ( x ) ; } , ( err ) => { log . debug ( err ) ; throw new Error ( "OpenSheetMusicDisplay: Invalid MXL file" ) ; } ) ; } if ( str . substr ( 0 , 3 ) === "\uf7ef\uf7bb\uf7bf" ) { log . debug ( "[OSMD] UTF with BOM detected, truncate first three bytes and pass along: " + str ) ; return self . load ( str . substr ( 3 ) ) ; } let trimmedStr = str ; if ( /^\s/ . test ( trimmedStr ) ) { trimmedStr = trimmedStr . trim ( ) ; } if ( trimmedStr . substr ( 0 , 6 ) . includes ( "<?xml" ) ) { const modifiedXml = this . OnXMLRead ( trimmedStr ) ; log . debug ( "[OSMD] Finally parsing XML content, length: " + modifiedXml . length ) ; const parser = new DOMParser ( ) ; content = parser . parseFromString ( modifiedXml , "application/xml" ) ; } else if ( trimmedStr . length < 2083 ) { log . debug ( "[OSMD] Retrieve the file at the given URL: " + trimmedStr ) ; return AJAX . ajax ( trimmedStr , this . loadUrlTimeout ) . then ( ( s ) => { return self . load ( s ) ; } , ( exc ) => { throw exc ; } ) ; } else { console . error ( "[OSMD] osmd.load(string): Could not process string. Did not find <?xml at beginning." ) ; } } if ( ! content || ! ( < any > content ) . nodeName ) { return Promise . reject ( new Error ( "OpenSheetMusicDisplay: The document which was provided is invalid" ) ) ; } const xmlDocument = ( < Document > content ) ; const xmlDocumentNodes = xmlDocument . childNodes ; log . debug ( "[OSMD] load(), Document url: " + xmlDocument . URL ) ; let scorePartwiseElement ; for ( let i = 0 , length = xmlDocumentNodes . length ; i < length ; i += 1 ) { const node = xmlDocumentNodes [ i ] ; if ( node . nodeType === Node . ELEMENT_NODE && node . nodeName . toLowerCase ( ) === "score-partwise" ) { scorePartwiseElement = < Element > node ; break ; } } if ( ! scorePartwiseElement ) { console . error ( "Could not parse MusicXML, no valid partwise element found" ) ; return Promise . reject ( new Error ( "OpenSheetMusicDisplay: Document is not a valid 'partwise' MusicXML" ) ) ; } const score = new IXmlElement ( scorePartwiseElement ) ; const reader = new MusicSheetReader ( undefined , this . rules ) ; this . sheet = reader . createMusicSheet ( score , "Untitled Score" ) ; if ( this . sheet === undefined ) { return Promise . reject ( new Error ( "given music sheet was incomplete or could not be loaded." ) ) ; } log . info ( `[OSMD] Loaded sheet ${ this . sheet . TitleString } successfully.` ) ; this . needBackendUpdate = true ; this . updateGraphic ( ) ; return Promise . resolve ( { } ) ; } /**
     * (Re-)creates the graphic sheet from the music sheet
     */ public updateGraphic ( ) { const calc = new VexFlowMusicSheetCalculator ( this . rules ) ; this . graphic = new GraphicalMusicSheet ( this . sheet , calc ) ; if ( this . drawingParameters . drawCursors ) { this . cursors . forEach ( cursor => { cursor . init ( this . sheet . MusicPartManager , this . graphic ) ; } ) ; } } /**
     * Render the music sheet in the container
     */ public render ( ) { if ( ! this . graphic ) { throw new Error ( "OpenSheetMusicDisplay: Before rendering a music sheet, please load a MusicXML file" ) ; } this . drawer ?. clear ( ) ; let width = this . container . offsetWidth ; if ( this . rules . RenderSingleHorizontalStaffline ) { width = 32767 ; } this . sheet . pageWidth = width / this . zoom / 10.0 ; if ( this . rules . PageFormat && ! this . rules . PageFormat . IsUndefined ) { this . rules . PageHeight = this . sheet . pageWidth / this . rules . PageFormat . aspectRatio ; log . debug ( "[OSMD] PageHeight: " + this . rules . PageHeight ) ; } else { log . debug ( "[OSMD] endless/undefined pageformat, id: " + this . rules . PageFormat . idString ) ; this . rules . PageHeight = 100001 ; } this . graphic . reCalculate ( ) ; if ( this . drawingParameters . drawCursors ) { this . graphic . Cursors . length = 0 ; } if ( true || this . needBackendUpdate ) { this . createOrRefreshRenderBackend ( ) ; this . needBackendUpdate = false ; } this . drawer . setZoom ( this . zoom ) ; this . drawer . drawSheet ( this . graphic ) ; this . enableOrDisableCursors ( this . drawingParameters . drawCursors ) ; if ( this . drawingParameters . drawCursors ) { this . cursors . forEach ( cursor => { cursor . update ( ) ; } ) ; } this . zoomUpdated = false ; } protected createOrRefreshRenderBackend ( ) { if ( this . drawer && this . drawer . Backends ) { if ( this . drawer . Backends [ 0 ] ) { this . drawer . Backends [ 0 ] . removeAllChildrenFromContainer ( this . container ) ; } this . drawer . Backends . clear ( ) ; } this . drawingParameters . Rules = this . rules ; this . drawer = new VexFlowMusicSheetDrawer ( this . drawingParameters ) ; this . drawer . drawableBoundingBoxElement = this . DrawBoundingBox ; this . drawer . bottomLineVisible = this . drawBottomLine ; this . drawer . skyLineVisible = this . drawSkyLine ; let width = this . container . offsetWidth ; if ( this . rules . RenderSingleHorizontalStaffline ) { width = this . graphic . MusicPages [ 0 ] . PositionAndShape . Size . width * 10 * this . zoom ; } let height ; const canvasDimensionsLimit = 32767 ; for ( const page of this . graphic . MusicPages ) { if ( page . PageNumber > this . rules . MaxPageToDrawNumber ) { break ; } const backend = this . createBackend ( this . backendType , page ) ; const sizeWarningPartTwo = " exceeds CanvasBackend limit of 32767. Cutting off score." ; if ( backend . getOSMDBackendType ( ) === BackendType . Canvas && width > canvasDimensionsLimit ) { log . warn ( "[OSMD] Warning: width of " + width + sizeWarningPartTwo ) ; width = canvasDimensionsLimit ; } if ( this . rules . PageFormat && ! this . rules . PageFormat . IsUndefined ) { height = width / this . rules . PageFormat . aspectRatio ; } else { height = page . PositionAndShape . Size . height ; height += this . rules . PageBottomMargin ; height += this . rules . CompactMode ? this . rules . PageTopMarginNarrow : this . rules . PageTopMargin ; if ( this . rules . RenderTitle ) { height += this . rules . TitleTopDistance ; } height *= this . zoom * 10.0 ; } if ( backend . getOSMDBackendType ( ) === BackendType . Canvas && height > canvasDimensionsLimit ) { log . warn ( "[OSMD] Warning: height of " + height + sizeWarningPartTwo ) ; height = Math . min ( height , canvasDimensionsLimit ) ; } backend . resize ( width , height ) ; backend . clear ( ) ; this . drawer . Backends . push ( backend ) ; this . graphic . drawer = this . drawer ; } } public exportSVG ( ) { for ( const backend of this . drawer ?. Backends ) { if ( backend instanceof SvgVexFlowBackend ) { ( backend as SvgVexFlowBackend ) . export ( ) ; } } } /** States whether the render() function can be safely called. */ public IsReadyToRender ( ) { return this . graphic !== undefined ; } /** Clears what OSMD has drawn on its canvas. */ public clear ( ) { this . drawer . clear ( ) ; this . reset ( ) ; } /** Set OSMD rendering options using an IOSMDOptions object.
     *  Can be called during runtime. Also called by constructor.
     *  For example, setOptions({autoResize: false}) will disable autoResize even during runtime.
     */ public setOptions ( options ) { if ( ! this . rules ) { this . rules = new EngravingRules ( ) ; } if ( ! this . drawingParameters ) { this . drawingParameters = new DrawingParameters ( ) ; this . drawingParameters . Rules = this . rules ; } if ( options === undefined || options === null ) { log . warn ( "warning: osmd.setOptions() called without an options parameter, has no effect." + "\n" + "example usage: osmd.setOptions({drawCredits: false, drawPartNames: false})" ) ; return ; } this . OnXMLRead = function ( xml ) : string { return xml ; } ; if ( options . onXMLRead ) { this . OnXMLRead = options . onXMLRead ; } if ( options . drawingParameters ) { this . drawingParameters . DrawingParametersEnum = ( < any > DrawingParametersEnum ) [ options . drawingParameters . toLowerCase ( ) ] ; } const backendNotInitialized = ! this . drawer || ! this . drawer . Backends || this . drawer . Backends . length < 1 ; let needBackendUpdate = backendNotInitialized ; if ( options . backend !== undefined ) { const backendTypeGiven = OSMDOptions . BackendTypeFromString ( options . backend ) ; needBackendUpdate = needBackendUpdate || this . backendType !== backendTypeGiven ; this . backendType = backendTypeGiven ; } this . needBackendUpdate = needBackendUpdate ; if ( options . autoBeam !== undefined ) { this . rules . AutoBeamNotes = options . autoBeam ; } const autoBeamOptions = options . autoBeamOptions ; if ( autoBeamOptions ) { if ( autoBeamOptions . maintain_stem_directions === undefined ) { autoBeamOptions . maintain_stem_directions = false ; } this . rules . AutoBeamOptions = autoBeamOptions ; if ( autoBeamOptions . groups && autoBeamOptions . groups . length ) { for ( const fraction of autoBeamOptions . groups ) { if ( fraction . length !== 2 ) { throw new Error ( "Each fraction in autoBeamOptions.groups must be of length 2, e.g. [3,4] for beaming three fourths" ) ; } } } } if ( options . percussionOneLineCutoff !== undefined ) { this . rules . PercussionOneLineCutoff = options . percussionOneLineCutoff ; } if ( this . rules . PercussionOneLineCutoff !== 0 && options . percussionForceVoicesOneLineCutoff !== undefined ) { this . rules . PercussionForceVoicesOneLineCutoff = options . percussionForceVoicesOneLineCutoff ; } if ( options . alignRests !== undefined ) { this . rules . AlignRests = options . alignRests ; } if ( options . coloringMode !== undefined ) { this . setColoringMode ( options ) ; } if ( options . coloringEnabled !== undefined ) { this . rules . ColoringEnabled = options . coloringEnabled ; } if ( options . colorStemsLikeNoteheads !== undefined ) { this . rules . ColorStemsLikeNoteheads = options . colorStemsLikeNoteheads ; } if ( options . disableCursor ) { this . drawingParameters . drawCursors = false ; } if ( options . drawHiddenNotes ) { this . drawingParameters . drawHiddenNotes = true ; } if ( options . drawCredits !== undefined ) { this . drawingParameters . DrawCredits = options . drawCredits ; } if ( options . drawComposer !== undefined ) { this . drawingParameters . DrawComposer = options . drawComposer ; } if ( options . drawTitle !== undefined ) { this . drawingParameters . DrawTitle = options . drawTitle ; } if ( options . drawSubtitle !== undefined ) { this . drawingParameters . DrawSubtitle = options . drawSubtitle ; } if ( options . drawLyricist !== undefined ) { this . drawingParameters . DrawLyricist = options . drawLyricist ; } if ( options . drawMetronomeMarks !== undefined ) { this . rules . MetronomeMarksDrawn = options . drawMetronomeMarks ; } if ( options . drawPartNames !== undefined ) { this . drawingParameters . DrawPartNames = options . drawPartNames ; if ( ! options . drawPartAbbreviations ) { this . rules . RenderPartAbbreviations = options . drawPartNames ; } } if ( options . drawPartAbbreviations !== undefined ) { this . rules . RenderPartAbbreviations = options . drawPartAbbreviations ; } if ( options . drawFingerings === false ) { this . rules . RenderFingerings = false ; } if ( options . drawMeasureNumbers !== undefined ) { this . rules . RenderMeasureNumbers = options . drawMeasureNumbers ; } if ( options . drawMeasureNumbersOnlyAtSystemStart ) { this . rules . RenderMeasureNumbersOnlyAtSystemStart = options . drawMeasureNumbersOnlyAtSystemStart ; } if ( options . drawLyrics !== undefined ) { this . rules . RenderLyrics = options . drawLyrics ; } if ( options . drawTimeSignatures !== undefined ) { this . rules . RenderTimeSignatures = options . drawTimeSignatures ; } if ( options . drawSlurs !== undefined ) { this . rules . RenderSlurs = options . drawSlurs ; } if ( options . measureNumberInterval !== undefined ) { this . rules . MeasureNumberLabelOffset = options . measureNumberInterval ; } if ( options . useXMLMeasureNumbers !== undefined ) { this . rules . UseXMLMeasureNumbers = options . useXMLMeasureNumbers ; } if ( options . fingeringPosition !== undefined ) { this . rules . FingeringPosition = AbstractExpression . PlacementEnumFromString ( options . fingeringPosition ) ; } if ( options . fingeringInsideStafflines !== undefined ) { this . rules . FingeringInsideStafflines = options . fingeringInsideStafflines ; } if ( options . newSystemFromXML !== undefined ) { this . rules . NewSystemAtXMLNewSystemAttribute = options . newSystemFromXML ; } if ( options . newPageFromXML !== undefined ) { this . rules . NewPageAtXMLNewPageAttribute = options . newPageFromXML ; } if ( options . fillEmptyMeasuresWithWholeRest !== undefined ) { this . rules . FillEmptyMeasuresWithWholeRest = options . fillEmptyMeasuresWithWholeRest ; } if ( options . followCursor !== undefined ) { this . FollowCursor = options . followCursor ; } if ( options . setWantedStemDirectionByXml !== undefined ) { this . rules . SetWantedStemDirectionByXml = options . setWantedStemDirectionByXml ; } if ( options . darkMode ) { this . rules . applyDefaultColorMusic ( "#FFFFFF" ) ; this . rules . PageBackgroundColor = "#000000" ; } else if ( options . darkMode === false ) { this . rules . applyDefaultColorMusic ( "#000000" ) ; this . rules . PageBackgroundColor = undefined ; } if ( options . defaultColorMusic ) { this . rules . applyDefaultColorMusic ( options . defaultColorMusic ) ; } if ( options . defaultColorNotehead ) { this . rules . DefaultColorNotehead = options . defaultColorNotehead ; } if ( options . defaultColorRest ) { this . rules . DefaultColorRest = options . defaultColorRest ; } if ( options . defaultColorStem ) { this . rules . DefaultColorStem = options . defaultColorStem ; } if ( options . defaultColorLabel ) { this . rules . DefaultColorLabel = options . defaultColorLabel ; } if ( options . defaultColorTitle ) { this . rules . DefaultColorTitle = options . defaultColorTitle ; } if ( options . defaultFontFamily ) { this . rules . DefaultFontFamily = options . defaultFontFamily ; } if ( options . defaultFontStyle ) { this . rules . DefaultFontStyle = options . defaultFontStyle ; } if ( options . drawUpToMeasureNumber ) { this . rules . MaxMeasureToDrawIndex = options . drawUpToMeasureNumber - 1 ; } if ( options . drawFromMeasureNumber ) { this . rules . MinMeasureToDrawIndex = options . drawFromMeasureNumber - 1 ; } if ( options . drawUpToPageNumber ) { this . rules . MaxPageToDrawNumber = options . drawUpToPageNumber ; } if ( options . drawUpToSystemNumber ) { this . rules . MaxSystemToDrawNumber = options . drawUpToSystemNumber ; } if ( options . tupletsRatioed ) { this . rules . TupletsRatioed = true ; } if ( options . tupletsBracketed ) { this . rules . TupletsBracketed = true ; } if ( options . tripletsBracketed ) { this . rules . TripletsBracketed = true ; } if ( options . autoResize ) { if ( ! this . resizeHandlerAttached ) { this . autoResize ( ) ; } this . autoResizeEnabled = true ; } else if ( options . autoResize === false ) { this . autoResizeEnabled = false ; } if ( options . pageFormat !== undefined ) { this . setPageFormat ( options . pageFormat ) ; } if ( options . pageBackgroundColor !== undefined ) { this . rules . PageBackgroundColor = options . pageBackgroundColor ; } if ( options . renderSingleHorizontalStaffline !== undefined ) { this . rules . RenderSingleHorizontalStaffline = options . renderSingleHorizontalStaffline ; } if ( options . spacingFactorSoftmax !== undefined ) { this . rules . SoftmaxFactorVexFlow = options . spacingFactorSoftmax ; } if ( options . spacingBetweenTextLines !== undefined ) { this . rules . SpacingBetweenTextLines = options . spacingBetweenTextLines ; } if ( options . stretchLastSystemLine !== undefined ) { this . rules . StretchLastSystemLine = options . stretchLastSystemLine ; } if ( options . autoGenerateMutipleRestMeasuresFromRestMeasures !== undefined ) { this . rules . AutoGenerateMutipleRestMeasuresFromRestMeasures = options . autoGenerateMutipleRestMeasuresFromRestMeasures ; } if ( options . cursorsOptions !== undefined ) { this . cursorsOptions = options . cursorsOptions ; } else { this . cursorsOptions = [ { type : 0 , color : this . EngravingRules . DefaultColorCursor , alpha : 0.5 , follow : true } ] ; } } public setColoringMode ( options ) { if ( options . coloringMode === ColoringModes . XML ) { this . rules . ColoringMode = ColoringModes . XML ; return ; } const noteIndices = [ NoteEnum . C , NoteEnum . D , NoteEnum . E , NoteEnum . F , NoteEnum . G , NoteEnum . A , NoteEnum . B , - 1 ] ; let colorSetString ; if ( options . coloringMode === ColoringModes . CustomColorSet ) { if ( ! options . coloringSetCustom || options . coloringSetCustom . length !== 8 ) { throw new Error ( "Invalid amount of colors: With coloringModes.customColorSet, " + "you have to provide a coloringSetCustom parameter with 8 strings (C to B, rest note)." ) ; } for ( const colorString of options . coloringSetCustom ) { const regExp = /^\#[0-9a-fA-F]{6}$/ ; if ( ! regExp . test ( colorString ) ) { throw new Error ( "One of the color strings in options.coloringSetCustom was not a valid HTML Hex color:\n" + colorString ) ; } } colorSetString = options . coloringSetCustom ; } else if ( options . coloringMode === ColoringModes . AutoColoring ) { colorSetString = [ ] ; const keys = Object . keys ( AutoColorSet ) ; for ( let i = 0 ; i < keys . length ; i ++ ) { colorSetString . push ( AutoColorSet [ keys [ i ] ] ) ; } } const coloringSetCurrent = new Dictionary < NoteEnum | number , string > ( ) ; for ( let i = 0 ; i < noteIndices . length ; i ++ ) { coloringSetCurrent . setValue ( noteIndices [ i ] , colorSetString [ i ] ) ; } coloringSetCurrent . setValue ( - 1 , colorSetString [ 7 ] ) ; this . rules . ColoringSetCurrent = coloringSetCurrent ; this . rules . ColoringMode = options . coloringMode ; } param public setLogLevel ( level ) { switch ( level ) { case "trace" : log . setLevel ( log . levels . TRACE ) ; break ; case "debug" : log . setLevel ( log . levels . DEBUG ) ; break ; case "info" : log . setLevel ( log . levels . INFO ) ; break ; case "warn" : log . setLevel ( log . levels . WARN ) ; break ; case "error" : log . setLevel ( log . levels . ERROR ) ; break ; case "silent" : log . setLevel ( log . levels . SILENT ) ; break ; default : log . warn ( `Could not set log level to ${ level }. Using warn instead.` ) ; log . setLevel ( log . levels . WARN ) ; break ; } } public getLogLevel ( ) { return log . getLevel ( ) ; } /**
     * Initialize this object to default values
     * FIXME: Probably unnecessary
     */ protected reset ( ) { if ( this . drawingParameters . drawCursors ) { this . cursors . forEach ( cursor => { cursor . hide ( ) ; } ) ; } this . sheet = undefined ; this . graphic = undefined ; this . zoom = 1.0 ; } /**
     * Attach the appropriate handler to the window.onResize event
     */ protected autoResize ( ) { const self = this ; this . handleResize ( ( ) => { } , ( ) => { if ( this . graphic ?. GetCalculator instanceof VexFlowMusicSheetCalculator ) { ( this . graphic . GetCalculator as VexFlowMusicSheetCalculator ) . beamsNeedUpdate = true ; } if ( self . IsReadyToRender ( ) ) { self . render ( ) ; } } ) ; } param startCallback param endCallback protected handleResize ( startCallback , endCallback ) { let rtime ; let timeout = undefined ; const delta = 200 ; const self = this ; function resizeStart ( ) { if ( ! self . AutoResizeEnabled ) { return ; } rtime = ( new Date ( ) ) . getTime ( ) ; if ( ! timeout ) { startCallback ( ) ; rtime = ( new Date ( ) ) . getTime ( ) ; timeout = window . setTimeout ( resizeEnd , delta ) ; } } function resizeEnd ( ) { timeout = undefined ; window . clearTimeout ( timeout ) ; if ( ( new Date ( ) ) . getTime ( ) - rtime < delta ) { timeout = window . setTimeout ( resizeEnd , delta ) ; } else { endCallback ( ) ; } } if ( ( < any > window ) . attachEvent ) { ( < any > window ) . attachEvent ( "onresize" , resizeStart ) ; } else { window . addEventListener ( "resize" , resizeStart ) ; } this . resizeHandlerAttached = true ; window . setTimeout ( startCallback , 0 ) ; window . setTimeout ( endCallback , 1 ) ; } param enable public enableOrDisableCursors ( enable ) { this . drawingParameters . drawCursors = enable ; if ( enable ) { for ( let i = 0 ; i < this . cursorsOptions . length ; i ++ ) { const hidden = this . cursors [ i ] ?. Hidden ; const previousIterator = this . cursors [ i ] ?. Iterator ; this . cursors [ i ] ?. hide ( ) ; let backendToDrawOn = this . drawer ?. Backends [ 0 ] ; if ( backendToDrawOn && this . rules . RestoreCursorAfterRerender && this . cursors [ i ] ) { const newPageNumber = this . cursors [ i ] . updateCurrentPage ( ) ; backendToDrawOn = this . drawer . Backends [ newPageNumber - 1 ] ; } if ( backendToDrawOn && backendToDrawOn . getRenderElement ( ) ) { this . cursors [ i ] = new Cursor ( backendToDrawOn . getRenderElement ( ) , this , this . cursorsOptions [ i ] ) ; } if ( this . sheet && this . graphic && this . cursors [ i ] ) { this . cursors [ i ] . init ( this . sheet . MusicPartManager , this . graphic ) ; } if ( this . rules . RestoreCursorAfterRerender ) { this . cursors [ i ] . hidden = hidden ; if ( previousIterator ) { this . cursors [ i ] . iterator = previousIterator ; this . cursors [ i ] . update ( ) ; } } } } else { this . cursors . forEach ( cursor => { cursor . hide ( ) ; } ) ; } } public createBackend ( type , page ) { let backend ; if ( type === undefined || type === BackendType . SVG ) { backend = new SvgVexFlowBackend ( this . rules ) ; } else { backend = new CanvasVexFlowBackend ( this . rules ) ; } backend . graphicalMusicPage = page ; backend . initialize ( this . container , this . zoom ) ; backend . getContext ( ) . setFillStyle ( this . rules . DefaultColorMusic ) ; backend . getContext ( ) . setStrokeStyle ( this . rules . DefaultColorMusic ) ; return backend ; } /** Standard page format options like A4 or Letter, in portrait and landscape. E.g. PageFormatStandards["A4_P"] or PageFormatStandards["Letter_L"]. */ public static PageFormatStandards : { [ type ] : PageFormat ; } = { "A3_L" : new PageFormat ( 420 , 297 , "A3_L" ) , "A3_P" : new PageFormat ( 297 , 420 , "A3_P" ) , "A4_L" : new PageFormat ( 297 , 210 , "A4_L" ) , "A4_P" : new PageFormat ( 210 , 297 , "A4_P" ) , "A5_L" : new PageFormat ( 210 , 148 , "A5_L" ) , "A5_P" : new PageFormat ( 148 , 210 , "A5_P" ) , "A6_L" : new PageFormat ( 148 , 105 , "A6_L" ) , "A6_P" : new PageFormat ( 105 , 148 , "A6_P" ) , "Endless" : PageFormat . UndefinedPageFormat , "Letter_L" : new PageFormat ( 279.4 , 215.9 , "Letter_L" ) , "Letter_P" : new PageFormat ( 215.9 , 279.4 , "Letter_P" ) } ; public static StringToPageFormat ( pageFormatString ) { let pageFormat = PageFormat . UndefinedPageFormat ; if ( pageFormatString . match ( "^[0-9]+x[0-9]+$" ) ) { const widthAndHeight = pageFormatString . split ( "x" ) ; const width = Number . parseInt ( widthAndHeight [ 0 ] , 10 ) ; const height = Number . parseInt ( widthAndHeight [ 1 ] , 10 ) ; if ( width > 0 && width < 32768 && height > 0 && height < 32768 ) { pageFormat = new PageFormat ( width , height , `customPageFormat${ pageFormatString }` ) ; } } pageFormatString = pageFormatString . replace ( " " , "_" ) ; pageFormatString = pageFormatString . replace ( "Landscape" , "L" ) ; pageFormatString = pageFormatString . replace ( "Portrait" , "P" ) ; if ( OpenSheetMusicDisplay . PageFormatStandards . hasOwnProperty ( pageFormatString ) ) { pageFormat = OpenSheetMusicDisplay . PageFormatStandards [ pageFormatString ] ; return pageFormat ; } return pageFormat ; } /** Sets page format by string. Used by setOptions({pageFormat: "A4_P"}) for example. */ public setPageFormat ( formatId ) { const newPageFormat = OpenSheetMusicDisplay . StringToPageFormat ( formatId ) ; this . needBackendUpdate = ! ( newPageFormat . Equals ( this . rules . PageFormat ) ) ; this . rules . PageFormat = newPageFormat ; } public setCustomPageFormat ( width , height ) { if ( width > 0 && height > 0 ) { const f = new PageFormat ( width , height ) ; this . rules . PageFormat = f ; } } public set DrawSkyLine ( value ) { this . drawSkyLine = value ; if ( this . drawer ) { this . drawer . skyLineVisible = value ; } } public get DrawSkyLine ( ) : boolean { return this . drawer . skyLineVisible ; } public set DrawBottomLine ( value ) { this . drawBottomLine = value ; if ( this . drawer ) { this . drawer . bottomLineVisible = value ; } } public get DrawBottomLine ( ) : boolean { return this . drawer . bottomLineVisible ; } public set DrawBoundingBox ( value ) { this . setDrawBoundingBox ( value , true ) ; } public get DrawBoundingBox ( ) : string { return this . drawBoundingBox ; } public setDrawBoundingBox ( value , render = false ) { this . drawBoundingBox = value ; if ( this . drawer ) { this . drawer . drawableBoundingBoxElement = value ; } if ( render ) { this . render ( ) ; } } public get AutoResizeEnabled ( ) : boolean { return this . autoResizeEnabled ; } public set AutoResizeEnabled ( value ) { this . autoResizeEnabled = value ; } public get Zoom ( ) : number { return this . zoom ; } public set Zoom ( value ) { this . zoom = value ; this . zoomUpdated = true ; if ( this . graphic ?. GetCalculator instanceof VexFlowMusicSheetCalculator ) { ( this . graphic . GetCalculator as VexFlowMusicSheetCalculator ) . beamsNeedUpdate = this . zoomUpdated ; } } public set FollowCursor ( value ) { this . followCursor = value ; } public get FollowCursor ( ) : boolean { return this . followCursor ; } public set TransposeCalculator ( calculator ) { MusicSheetCalculator . transposeCalculator = calculator ; } public get TransposeCalculator ( ) : ITransposeCalculator { return MusicSheetCalculator . transposeCalculator ; } public get Sheet ( ) : MusicSheet { return this . sheet ; } public get Drawer ( ) : VexFlowMusicSheetDrawer { return this . drawer ; } public get GraphicSheet ( ) : GraphicalMusicSheet { return this . graphic ; } public get DrawingParameters ( ) : DrawingParameters { return this . drawingParameters ; } public get EngravingRules ( ) : EngravingRules { return this . rules ; } /** Returns the version of OSMD this object is built from (the version you are using). */ public get Version ( ) : string { return this . version ; } }