@stringsync/vexml
Version:
MusicXML to Vexflow
298 lines (297 loc) • 12 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.EventMappingFactory = void 0;
const spatial = __importStar(require("../spatial"));
const util = __importStar(require("../util"));
const MOUSEDOWN_MOVEMENT_TOLERANCE = 10;
const LONGPRESS_DURATION_MS = 500;
class EventMappingFactory {
root;
elementLocator;
timestampLocator;
topic;
constructor(root, elementLocator, timestampLocator, topic) {
this.root = root;
this.elementLocator = elementLocator;
this.timestampLocator = timestampLocator;
this.topic = topic;
}
static create(root, elementLocator, timestampLocator, topic, inputType) {
return new EventMappingFactory(root, elementLocator, timestampLocator, topic).create(inputType);
}
create(inputType) {
switch (inputType) {
case 'mouse':
return [this.mousePress(), this.mouseEgress(), this.scroll()];
case 'touch':
return [this.touchPress(), this.touchEgress(), this.scroll()];
case 'hybrid':
return [...this.create('mouse'), ...this.create('touch')];
case 'auto':
switch (util.device().inputType) {
case 'mouseonly':
return this.create('mouse');
case 'touchonly':
return this.create('touch');
case 'hybrid':
return this.create('hybrid');
default:
return [];
}
default:
return [];
}
}
locate(clientPoint) {
const point = this.point(clientPoint);
const targets = this.elementLocator.locate(point);
const closestTarget = targets[0] ?? null;
const timestampMs = this.timestampLocator.locate(point)?.ms ?? null;
return { point, target: closestTarget, timestampMs };
}
point(clientPoint) {
const rect = this.root.getOverlay().getElement().getBoundingClientRect();
const x = clientPoint.clientX - rect.left;
const y = clientPoint.clientY - rect.top;
return new spatial.Point(x, y);
}
scroll() {
const scrollContainer = this.root.getScrollContainer();
return {
src: 'scroll',
vexml: ['scroll', 'scroll'],
native: {
scroll: (event) => {
this.topic.publish('scroll', {
type: 'scroll',
scrollX: scrollContainer.scrollLeft,
scrollY: scrollContainer.scrollTop,
native: event,
});
},
},
};
}
mousePress() {
let timeout = 0;
let lastMouseDownInvocation = Symbol();
let lastMouseDownPoint = spatial.Point.origin();
let isPending = false;
return {
src: 'overlay',
vexml: ['click', 'longpress'],
native: {
mousedown: (event) => {
const mouseDownInvocation = Symbol();
const { point, target: closestTarget, timestampMs } = this.locate(event);
if (!closestTarget) {
return;
}
lastMouseDownInvocation = mouseDownInvocation;
lastMouseDownPoint = point;
isPending = true;
timeout = setTimeout(() => {
if (lastMouseDownInvocation === mouseDownInvocation) {
this.topic.publish('longpress', {
type: 'longpress',
target: closestTarget,
point,
native: event,
timestampMs,
});
}
isPending = false;
}, LONGPRESS_DURATION_MS);
},
mousemove: (event) => {
if (isPending && lastMouseDownPoint.distance(this.point(event)) > MOUSEDOWN_MOVEMENT_TOLERANCE) {
clearTimeout(timeout);
isPending = false;
}
},
mouseup: (event) => {
if (isPending) {
const { point, target: closestTarget, timestampMs } = this.locate(event);
if (closestTarget) {
this.topic.publish('click', { type: 'click', target: closestTarget, point, native: event, timestampMs });
}
}
clearTimeout(timeout);
lastMouseDownInvocation = Symbol();
lastMouseDownPoint = spatial.Point.origin();
isPending = false;
},
},
};
}
mouseEgress() {
let lastResult = null;
return {
src: 'overlay',
vexml: ['enter', 'exit'],
native: {
mousemove: (event) => {
const result = this.locate(event);
if (lastResult && lastResult.target && lastResult.target !== result.target) {
this.topic.publish('exit', {
type: 'exit',
target: lastResult.target,
point: lastResult.point,
native: event,
timestampMs: lastResult.timestampMs,
});
}
if (result.target && result.target !== lastResult?.target) {
this.topic.publish('enter', {
type: 'enter',
target: result.target,
point: result.point,
native: event,
timestampMs: result.timestampMs,
});
}
lastResult = result;
},
},
};
}
touchPress() {
let timeout = undefined;
let isPending = false;
let lastTouchStartInvocation = Symbol();
return {
src: 'overlay',
vexml: ['click', 'longpress'],
native: {
touchstart: (event) => {
const touchStartInvocation = Symbol();
const { point, target: closestTarget, timestampMs } = this.locate(event.touches[0]);
if (!closestTarget) {
return;
}
lastTouchStartInvocation = touchStartInvocation;
isPending = true;
timeout = setTimeout(() => {
if (lastTouchStartInvocation === touchStartInvocation) {
this.topic.publish('longpress', {
type: 'longpress',
target: closestTarget,
point,
native: event,
timestampMs,
});
}
isPending = false;
}, LONGPRESS_DURATION_MS);
},
touchmove: () => {
clearTimeout(timeout);
isPending = false;
},
touchend: (event) => {
if (isPending) {
const { point, target: closestTarget, timestampMs } = this.locate(event.changedTouches[0]);
if (closestTarget) {
this.topic.publish('click', { type: 'click', target: closestTarget, point, native: event, timestampMs });
}
}
clearTimeout(timeout);
isPending = false;
},
touchcancel: () => {
clearTimeout(timeout);
isPending = false;
},
},
};
}
touchEgress() {
let lastResult = null;
return {
src: 'overlay',
vexml: ['enter', 'exit'],
native: {
touchmove: (event) => {
const result = this.locate(event.touches[0]);
if (lastResult && lastResult.target && lastResult.target !== result.target) {
this.topic.publish('exit', {
type: 'exit',
target: lastResult.target,
point: lastResult.point,
native: event,
timestampMs: lastResult.timestampMs,
});
}
if (result.target && result.target !== lastResult?.target) {
this.topic.publish('enter', {
type: 'enter',
target: result.target,
point: result.point,
native: event,
timestampMs: result.timestampMs,
});
lastResult = result;
}
},
touchcancel: (event) => {
if (lastResult?.target) {
this.topic.publish('exit', {
type: 'exit',
target: lastResult.target,
point: lastResult.point,
native: event,
timestampMs: lastResult.timestampMs,
});
lastResult = null;
}
},
touchend: (event) => {
if (lastResult?.target) {
this.topic.publish('exit', {
type: 'exit',
target: lastResult.target,
point: lastResult.point,
native: event,
timestampMs: lastResult.timestampMs,
});
lastResult = null;
}
},
},
};
}
}
exports.EventMappingFactory = EventMappingFactory;