Skip to content

Commit

Permalink
Walk up the prototype chain to check for .getCategory().
Browse files Browse the repository at this point in the history
  • Loading branch information
ronyeh committed Aug 18, 2021
1 parent e58e3fe commit c73b6ba
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 44 deletions.
19 changes: 7 additions & 12 deletions src/articulation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { ModifierContextState } from './modifiercontext';
import { Builder } from './easyscore';
import { TabNote } from './tabnote';
import { GraceNote } from './gracenote';
import { isTabNote } from 'typeguard';

export interface ArticulationStruct {
code?: string;
Expand Down Expand Up @@ -77,7 +78,7 @@ function getTopY(note: Note, textLine: number): number {
} else {
return Math.min(...note.getYs());
}
} else if (isTabNoteCategory(note)) {
} else if (isTabNote(note)) {
if (note.hasStem()) {
if (stemDirection === Stem.UP) {
return stemTipY;
Expand Down Expand Up @@ -106,7 +107,7 @@ function getBottomY(note: Note, textLine: number): number {
} else {
return Math.max(...note.getYs());
}
} else if (isTabNoteCategory(note)) {
} else if (isTabNote(note)) {
if (note.hasStem()) {
if (stemDirection === Stem.UP) {
return note.checkStave().getYForBottomText(textLine);
Expand Down Expand Up @@ -153,18 +154,12 @@ function getInitialOffset(note: Note, position: number): number {
}
}

//#region Helper functions for checking the type of Note objects.
// TODO: Should we replace these with isCategory from util.ts?
// Helper function for checking if a Note object is either a StaveNote or a GraceNote.
function isStaveOrGraceNoteCategory(note: Note): boolean {
const noteCategory = note.getCategory();
return noteCategory === StaveNote.CATEGORY || noteCategory === GraceNote.CATEGORY;
const category = note.getCategory();
return category === StaveNote.CATEGORY || category === GraceNote.CATEGORY;
}

function isTabNoteCategory(note: Note): boolean {
return note.getCategory() === TabNote.CATEGORY;
}
//#endregion

/**
* Articulations and Accents are modifiers that can be
* attached to notes. The complete list of articulations is available in
Expand Down Expand Up @@ -306,7 +301,7 @@ export class Articulation extends Modifier {

const stave = note.checkStave();
const staffSpace = stave.getSpacingBetweenLines();
const isTab = isTabNoteCategory(note);
const isTab = isTabNote(note);

// Articulations are centered over/under the note head.
const { x } = note.getModifierStartXY(position, index);
Expand Down
21 changes: 11 additions & 10 deletions src/modifiercontext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,8 @@ export interface ModifierContextMetrics {
export type ModifierContextMember = Modifier | StaveNote | TabNote;

// To enable logging for this class. Set `Vex.Flow.ModifierContext.DEBUG` to `true`.
function L(
// eslint-disable-next-line
...args: any[]) {
// eslint-disable-next-line
function L(...args: any[]) {
if (ModifierContext.DEBUG) log('Vex.Flow.ModifierContext', args);
}

Expand Down Expand Up @@ -107,21 +106,23 @@ export class ModifierContext {
}

addMember(member: ModifierContextMember): this {
const type = member.getCategory();
if (!this.members[type]) this.members[type] = [];
this.members[type].push(member);
const category = member.getCategory();
if (!this.members[category]) {
this.members[category] = [];
}
this.members[category].push(member);
member.setModifierContext(this);
this.preFormatted = false;
return this;
}

getModifiers(type: string): ModifierContextMember[] {
getModifiers(category: string): ModifierContextMember[] {
L('getModifiers is deprecated, use getMembers instead.');
return this.getMembers(type);
return this.getMembers(category);
}

getMembers(type: string): ModifierContextMember[] {
return this.members[type];
getMembers(category: string): ModifierContextMember[] {
return this.members[category];
}

getWidth(): number {
Expand Down
3 changes: 1 addition & 2 deletions src/ornament.ts
Original file line number Diff line number Diff line change
Expand Up @@ -260,8 +260,7 @@ export class Ornament extends Modifier {
const stemExtents = note.checkStem().getExtents();
let y = stemDir === StaveNote.STEM_DOWN ? stemExtents.baseY : stemExtents.topY;

// TabNotes don't have stems attached to them. Tab stems are rendered
// outside the stave.
// TabNotes don't have stems attached to them. Tab stems are rendered outside the stave.
if (note.getCategory() === TabNote.CATEGORY) {
if (note.hasStem()) {
if (stemDir === StaveNote.STEM_DOWN) {
Expand Down
45 changes: 34 additions & 11 deletions src/typeguard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,45 @@ import { TabNote } from 'tabnote';

/* eslint-disable */
type Constructor<T> = Function & { prototype: T };

/**
* Use this instead of `instanceof` as a more flexible type guard.
* @param ClassName
* @param obj
* @returns true if `obj` is likely an instance of `ClassName`.
* @param cls a JavaScript class, such as `StaveNote`.
* @param obj check if this object is an instance of the provided `cls`.
* @param checkAncestors defaults to `true`, so we walk up the prototype chain to look for a matching `.getCategory()`.
* If `false`, we do not check the superclass or other ancestors.
* @returns true if `obj` is an instance of `ClassName`, or has a `.getCategory()` that matches `ClassName.CATEGORY`.
*/
export function isCategory<T>(ClassName: Constructor<T>, obj: any): obj is T {
// e.g., if obj is undefined or a number
if (typeof obj !== 'object') {
export function isCategory<Class>(cls: Constructor<Class>, obj: any, checkAncestors: boolean = true): obj is Class {
// obj is NOT an instance of cls if it is:
// undefined, a number, a primitive string, or null.
if (typeof obj !== 'object' || obj === null) {
return false;
}

return (
obj instanceof ClassName ||
ClassName.name === obj.constructor.name ||
('getCategory' in obj && obj.getCategory() === (ClassName as any).CATEGORY)
);
if (obj instanceof cls || cls.name === obj.constructor.name) {
return true;
}

// Check for the getCategory() / .CATEGORY.
const categoryToMatch: string | undefined = (cls as any).CATEGORY;
if (categoryToMatch === undefined) {
return false;
}

if (checkAncestors) {
// Walk up the prototype chain to look for a matching .getCategory().
while (obj !== null) {
if ('getCategory' in obj && obj.getCategory() === categoryToMatch) {
return true;
}
obj = Object.getPrototypeOf(obj);
}
return false;
} else {
// Do not walk up the prototype chain. Just check this object's .getCategory().
return 'getCategory' in obj && obj.getCategory() === categoryToMatch;
}
}

export function isStaveNote(obj: any): obj is StaveNote {
Expand All @@ -29,4 +51,5 @@ export function isStaveNote(obj: any): obj is StaveNote {
export function isTabNote(obj: any): obj is TabNote {
return isCategory<TabNote>(TabNote, obj);
}

/* eslint-enable */
13 changes: 4 additions & 9 deletions src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,16 @@ export function check<T>(x?: T): T {
}

/** Default log function sends all arguments to console. */
export function log(
block: string,
// eslint-disable-next-line
...args: any[]
): void {
// eslint-disable-next-line
export function log(block: string, ...args: any[]): void {
if (!args) return;
const line = Array.prototype.slice.call(args).join(' ');
window.console.log(block + ': ' + line);
}

/** Dump warning to console. */
export function warn(
// eslint-disable-next-line
...args: any[]
): void {
// eslint-disable-next-line
export function warn(...args: any[]): void {
const line = args.join(' ');
const err = new Error();
window.console.log('Warning: ', line, err.stack);
Expand Down

0 comments on commit c73b6ba

Please sign in to comment.