diff --git a/src/editor/editor.js b/src/editor/editor.js index 4225aea..5d1c74b 100644 --- a/src/editor/editor.js +++ b/src/editor/editor.js @@ -231,9 +231,15 @@ export default class Editor } + static reduce_trackClear(state, action) + { + return { ...state, tracks: [] } + } + + static reduce_trackAdd(state, action) { - state = Track.handlerForTrackKind(action.kind).init(state, action) + state = Track.handlerForTrackKind(action.kind).init(state, null, action) state = Editor.Cursor.place(state, null, state.tracks.length - 1) return Editor.reduce_resize(state, { type: "resize", w: state.w, h: state.h }) diff --git a/src/editor/track.js b/src/editor/track.js index 225d9fd..b1de551 100644 --- a/src/editor/track.js +++ b/src/editor/track.js @@ -42,6 +42,13 @@ export default class Track if (!fn) return null + if (state.tracks[trackIndex].trackId) + { + const track = state.project.tracks.find(t => t.id === state.tracks[trackIndex].trackId) + if (!track) + return null + } + return fn(state, trackIndex, args) } diff --git a/src/editor/trackNotes.js b/src/editor/trackNotes.js index 7705d27..e0995da 100644 --- a/src/editor/trackNotes.js +++ b/src/editor/trackNotes.js @@ -16,6 +16,7 @@ export default class TrackNotes { ...Track.init(state, action), kind: "notes", + trackId: action.trackId, rowScale: 15, } @@ -112,7 +113,8 @@ export default class TrackNotes return state const id = hover.id - const note = state.project.notes.findById(id) + const track = state.project.tracks.find(t => t.id === state.tracks[trackIndex].trackId) + const note = track.notes.findById(id) if (!note) return state @@ -124,8 +126,9 @@ export default class TrackNotes static selectionAddAtCursor(state, trackIndex) { const timeRange = new Range(state.cursor.time1, state.cursor.time2, false, false).sorted() - - for (const note of state.project.notes.iterAtRange(timeRange)) + const track = state.project.tracks.find(t => t.id === state.tracks[trackIndex].trackId) + + for (const note of track.notes.iterAtRange(timeRange)) state = Track.selectionAdd(state, trackIndex, note.id) return state @@ -153,14 +156,15 @@ export default class TrackNotes static selectionRemoveConflictingBehind(state, trackIndex) { const selection = state.tracks[trackIndex].selection + const track = state.project.tracks.find(t => t.id === state.tracks[trackIndex].trackId) for (const id of selection) { - const elem = state.project.findById(id) + const elem = track.notes.findById(id) if (!elem) continue - for (const note of state.project.notes.iterAtRange(elem.range)) + for (const note of track.notes.iterAtRange(elem.range)) { if (selection.has(note.id)) continue @@ -168,13 +172,15 @@ export default class TrackNotes if (note.pitch !== elem.pitch) continue - state = { ...state, project: state.project.removeById(note.id) } + let project = state.project.upsertNote(note, true) for (const slice of note.range.iterSlices(elem.range)) { - const newNote = new Project.Note(slice, note.pitch) - state = { ...state, project: state.project.upsertNote(newNote) } + const newNote = new Project.Note(note.trackId, slice, note.pitch) + project = project.upsertNote(newNote) } + + state = { ...state, project } } } @@ -188,8 +194,9 @@ export default class TrackNotes { if (elem instanceof Project.Note) { + const trackId = state.tracks[trackIndex].trackId const range = elem.range.displace(pasteData.time.subtract(state.clipboard.range.start)) - const note = new Project.Note(range, elem.pitch) + const note = new Project.Note(trackId, range, elem.pitch) const id = state.project.nextId state = { ...state, project: state.project.upsertNote(note) } state = Track.selectionAdd(state, trackIndex, id) @@ -202,19 +209,25 @@ export default class TrackNotes static previousAnchor(state, trackIndex, time) { - return state.project.notes.findPreviousAnchor(time) + const trackId = state.tracks[trackIndex].trackId + const track = state.project.tracks.find(t => t.id === trackId) + + return track.notes.findPreviousAnchor(time) } static deleteRange(state, trackIndex, range) { - for (const note of state.project.notes.iterAtRange(range)) + const trackId = state.tracks[trackIndex].trackId + const track = state.project.tracks.find(t => t.id === trackId) + + for (const note of track.notes.iterAtRange(range)) { - state = { ...state, project: state.project.removeById(note.id) } + state = { ...state, project: state.project.upsertNote(note, true) } for (const slice of note.range.iterSlices(range)) { - const newNote = new Project.Note(slice, note.pitch) + const newNote = new Project.Note(trackId, slice, note.pitch) state = { ...state, project: state.project.upsertNote(newNote) } } } @@ -279,7 +292,8 @@ export default class TrackNotes soundPreview = TrackNotes.mergeSoundPreview(soundPreview, newPitch) } - state = { ...state, project: state.project.update(elem.withChanges(changes)) } + let project = state.project.upsertNote(elem.withChanges(changes)) + state = { ...state, project } } state = Editor.soundPreviewSet(state, soundPreview) @@ -311,7 +325,8 @@ export default class TrackNotes { if (draw.kind == "note") { - const note = new Project.Note(new Range(draw.time1, draw.time2).sorted(), draw.pitch) + const editorTrack = state.tracks[trackIndex] + const note = new Project.Note(editorTrack.trackId, new Range(draw.time1, draw.time2).sorted(), draw.pitch) const id = state.project.nextId state = { ...state, project: state.project.upsertNote(note) } state = Track.selectionAdd(state, trackIndex, id) @@ -334,7 +349,7 @@ export default class TrackNotes let changes = {} changes.range = elem.range.displace(commandData.timeDelta) - state = { ...state, project: state.project.update(elem.withChanges(changes)) } + state = { ...state, project: state.project.upsertNote(elem.withChanges(changes)) } } return state @@ -365,7 +380,7 @@ export default class TrackNotes changes.pitch = elem.pitch + commandData.pitchDelta soundPreview = TrackNotes.mergeSoundPreview(soundPreview, changes.pitch) - state = { ...state, project: state.project.update(elem.withChanges(changes)) } + state = { ...state, project: state.project.upsertNote(elem.withChanges(changes)) } } state = Editor.soundPreviewSet(state, soundPreview) @@ -454,6 +469,9 @@ export default class TrackNotes static *iterNotesAndKeyChangesAtRange(state, trackIndex, range) { const defaultKey = Editor.defaultKey() + const track = state.project.tracks.find(t => t.id === state.tracks[trackIndex].trackId) + if (!track) + return for (const pair of state.project.keyChanges.iterActiveAtRangePairwise(range)) { @@ -466,12 +484,39 @@ export default class TrackNotes const time1 = keyCh1.time.max(range.start) const time2 = keyCh2.time.min(range.end) - for (const note of state.project.notes.iterAtRange(new Range(time1, time2))) + for (const note of track.notes.iterAtRange(new Range(time1, time2))) yield [note, keyCh1, keyCh1X, keyCh2X] } } + static *iterOnionNotesAndKeyChangesAtRange(state, trackIndex, range) + { + const defaultKey = Editor.defaultKey() + + for (const pair of state.project.keyChanges.iterActiveAtRangePairwise(range)) + { + const keyCh1 = pair[0] || new Project.KeyChange(range.start, defaultKey) + const keyCh2 = pair[1] || new Project.KeyChange(range.end, defaultKey) + + const keyCh1X = Editor.xAtTime(state, keyCh1.time) + const keyCh2X = Editor.xAtTime(state, keyCh2.time) + + const time1 = keyCh1.time.max(range.start) + const time2 = keyCh2.time.min(range.end) + + for (const track of state.project.tracks) + { + if (track.id === state.tracks[trackIndex].trackId) + continue + + for (const note of track.notes.iterAtRange(new Range(time1, time2))) + yield [note, keyCh1, keyCh1X, keyCh2X] + } + } + } + + static render(state, trackIndex, ctx) { const track = state.tracks[trackIndex] @@ -517,6 +562,17 @@ export default class TrackNotes } } + for (const [note, keyCh, xMin, xMax] of TrackNotes.iterOnionNotesAndKeyChangesAtRange(state, trackIndex, visibleTimeRange)) + { + const row = TrackNotes.rowForPitch(state, trackIndex, note.pitch, keyCh.key) + const mode = keyCh.key.scale.metadata.mode + const fillStyle = CanvasUtils.fillStyleForDegree(ctx, keyCh.key.degreeForMidi(note.pitch) + mode) + const hovering = track.hover && track.hover.id == note.id + const playing = state.playback.playing && note.range.overlapsPoint(state.playback.time) + + TrackNotes.renderOutlineNote(state, trackIndex, ctx, note.range, row, xMin, xMax, fillStyle, 0.25, hovering, false, playing) + } + for (const [note, keyCh, xMin, xMax] of TrackNotes.iterNotesAndKeyChangesAtRange(state, trackIndex, visibleTimeRange)) { if (!state.playback.playing && track.selection.has(note.id)) @@ -527,7 +583,7 @@ export default class TrackNotes const fillStyle = CanvasUtils.fillStyleForDegree(ctx, keyCh.key.degreeForMidi(note.pitch) + mode) const hovering = track.hover && track.hover.id == note.id const playing = state.playback.playing && note.range.overlapsPoint(state.playback.time) - TrackNotes.renderNote(state, trackIndex, ctx, note.range, row, xMin, xMax, fillStyle, hovering, false, playing) + TrackNotes.renderNote(state, trackIndex, ctx, note.range, row, xMin, xMax, fillStyle, 1, hovering, false, playing) } if (!state.playback.playing) @@ -541,7 +597,7 @@ export default class TrackNotes const mode = keyCh.key.scale.metadata.mode const fillStyle = CanvasUtils.fillStyleForDegree(ctx, keyCh.key.degreeForMidi(note.pitch) + mode) const hovering = track.hover && track.hover.id == note.id - TrackNotes.renderNote(state, trackIndex, ctx, note.range, row, xMin, xMax, fillStyle, hovering, true, false) + TrackNotes.renderNote(state, trackIndex, ctx, note.range, row, xMin, xMax, fillStyle, 1, hovering, true, false) } } @@ -555,25 +611,26 @@ export default class TrackNotes const row = TrackNotes.rowForPitch(state, trackIndex, draw.pitch, key) const mode = key.scale.metadata.mode const fillStyle = CanvasUtils.fillStyleForDegree(ctx, key.degreeForMidi(draw.pitch) + mode) - TrackNotes.renderNote(state, trackIndex, ctx, new Range(draw.time1, draw.time2).sorted(), row, -Infinity, Infinity, fillStyle) + TrackNotes.renderNote(state, trackIndex, ctx, new Range(draw.time1, draw.time2).sorted(), row, -Infinity, Infinity, fillStyle, 1) ctx.globalAlpha = 1 } } - static renderNote(state, trackIndex, ctx, range, row, xMin, xMax, fillStyle, hovering, selected, playing) + static renderNote(state, trackIndex, ctx, range, row, xMin, xMax, fillStyle, alpha, hovering, selected, playing) { const rect = TrackNotes.rectForNote(state, trackIndex, range, row, xMin, xMax) ctx.fillStyle = fillStyle - + ctx.globalAlpha = alpha + ctx.beginPath() ctx.fillRect(rect.x, rect.y, rect.w, rect.h) if (hovering) { - ctx.globalAlpha = 0.4 + ctx.globalAlpha = alpha * 0.4 ctx.fillStyle = "#fff" ctx.fillRect(rect.x, rect.y, rect.w, rect.h) ctx.globalAlpha = 1 @@ -582,10 +639,44 @@ export default class TrackNotes if (selected || playing) { const margin = 3 - ctx.globalAlpha = 0.6 + ctx.globalAlpha = alpha * 0.6 ctx.fillStyle = "#fff" ctx.fillRect(rect.x, rect.y + margin, rect.w, rect.h - margin * 2) ctx.globalAlpha = 1 } + + ctx.globalAlpha = 1 + } + + + static renderOutlineNote(state, trackIndex, ctx, range, row, xMin, xMax, fillStyle, alpha, hovering, selected, playing) + { + const rect = TrackNotes.rectForNote(state, trackIndex, range, row, xMin, xMax) + + ctx.strokeStyle = fillStyle + ctx.lineWidth = 3 + ctx.globalAlpha = alpha + + ctx.beginPath() + ctx.strokeRect(rect.x, rect.y, rect.w, rect.h) + + /*if (hovering) + { + ctx.globalAlpha = alpha * 0.4 + ctx.fillStyle = "#fff" + ctx.strokeRect(rect.x, rect.y, rect.w, rect.h) + ctx.globalAlpha = 1 + } + + if (selected || playing) + { + const margin = 3 + ctx.globalAlpha = alpha * 0.6 + ctx.fillStyle = "#fff" + ctx.strokeRect(rect.x, rect.y + margin, rect.w, rect.h - margin * 2) + ctx.globalAlpha = 1 + }*/ + + ctx.globalAlpha = 1 } } \ No newline at end of file diff --git a/src/project/ioMidi.js b/src/project/ioMidi.js index 63a2bf2..1864d97 100644 --- a/src/project/ioMidi.js +++ b/src/project/ioMidi.js @@ -28,9 +28,20 @@ export default class IOMidi const msPerQuarterNote = (tempoEv ? tempoEv.msPerQuarterNote : 500000) song.baseBpm = Math.round(60 * 1000 * 1000 / msPerQuarterNote) - let notesToAdd = [] for (const track of midi.tracks) { + let trackName = "New Track" + for (const ev of track.events) + { + if (ev.kind == "trackName") + trackName = ev.name + } + + const trackId = song.nextId + song = song.upsertTrack(new Project.Track().withChanges({ name: trackName })) + + let notesToAdd = [] + for (const noteOn of track.events) { if (noteOn.kind != "noteOn" || noteOn.channel == 9 || noteOn.velocity == 0) @@ -58,11 +69,12 @@ export default class IOMidi const onTick = Rational.fromFloat(noteOn.time / midi.ticksPerQuarterNote / 4, 27720) const offTick = Rational.fromFloat(noteOff.time / midi.ticksPerQuarterNote / 4, 27720) - notesToAdd.push(new Project.Note(new Range(onTick, offTick), noteOn.key)) + notesToAdd.push(new Project.Note(trackId, new Range(onTick, offTick), noteOn.key)) } + + song = song.upsertNotes(notesToAdd) } - song = song.upsertNotes(notesToAdd) for (const track of midi.tracks) { diff --git a/src/project/playbackSynth.js b/src/project/playbackSynth.js index bfcf0dc..2db0d67 100644 --- a/src/project/playbackSynth.js +++ b/src/project/playbackSynth.js @@ -32,14 +32,17 @@ export default class PlaybackSynth } // Register notes. - for (const note of project.notes.iterAll()) + for (const track of project.tracks) { - if (note.range.end.compare(startTick) <= 0) - continue - - addNoteEvent(note.range.start, note.range.duration, 0, note.pitch, 1) + for (const note of track.notes.iterAll()) + { + if (note.range.end.compare(startTick) <= 0) + continue + + addNoteEvent(note.range.start, note.range.duration, 0, note.pitch, 1) + } } - + // Register chords. for (const chord of project.chords.iterAll()) { diff --git a/src/project/project.js b/src/project/project.js index a02f4c6..e469086 100644 --- a/src/project/project.js +++ b/src/project/project.js @@ -6,6 +6,7 @@ import Theory from "../theory.js" import IOJson from "./ioJson.js" import IOMidi from "./ioMidi.js" import IOCompressedStr from "./ioCompressedStr.js" +import { default as Immutable } from "immutable" export default class Project @@ -15,7 +16,7 @@ export default class Project this.nextId = 1 this.baseBpm = 120 this.range = new Range(new Rational(0), new Rational(4)) - this.notes = new ListOfRanges() + this.tracks = [] this.chords = new ListOfRanges() this.meterChanges = new ListOfPoints() this.keyChanges = new ListOfPoints() @@ -27,6 +28,7 @@ export default class Project return new Project() .upsertKeyChange(new Project.KeyChange(new Rational(0, 4), Theory.Key.parse("C Major"))) .upsertMeterChange(new Project.MeterChange(new Rational(0, 4), new Theory.Meter(4, 4))) + .upsertTrack(new Project.Track()) .withRefreshedRange() } @@ -37,7 +39,7 @@ export default class Project song.nextId = this.nextId song.baseBpm = this.baseBpm song.range = this.range - song.notes = this.notes + song.tracks = this.tracks song.chords = this.chords song.meterChanges = this.meterChanges song.keyChanges = this.keyChanges @@ -49,7 +51,10 @@ export default class Project withRefreshedRange() { let range = null - range = Range.merge(range, this.notes.getTotalRange()) + + for (const track of this.tracks.values()) + range = Range.merge(range, track.notes.getTotalRange()) + range = Range.merge(range, this.chords.getTotalRange()) range = Range.merge(range, this.meterChanges.getTotalRange()) range = Range.merge(range, this.keyChanges.getTotalRange()) @@ -68,6 +73,56 @@ export default class Project } + _upsertTrack(track, remove = false) + { + let nextId = this.nextId + let list = this.tracks + + if (track.id < 0) + { + track = Object.assign({}, track, { id: nextId }) + nextId++ + } + + if (!remove) + { + const index = list.findIndex(t => t.id === track.id) + if (index < 0) + list = [ ...list, track ] + else + list = [ ...list.slice(0, index), track, ...list.slice(index + 1) ] + } + else + list = list.filter(t => t.id !== track.id) + + return this.withChanges({ nextId, tracks: list }) + } + + + _upsertTrackElement(track, listField, elem, remove = false) + { + let nextId = this.nextId + let list = track[listField] + + if (elem.id < 0) + { + elem = elem.withChanges({ id: nextId }) + nextId++ + } + else + { + list = list.removeById(elem.id) + } + + if (!remove) + list = list.add(elem) + + let res = this.withChanges({ nextId }) + res = res._upsertTrack({ ...track, [listField]: list }) + return res + } + + _upsertElement(listField, elem, remove = false) { let nextId = this.nextId @@ -120,13 +175,25 @@ export default class Project upsertNote(note, remove = false) { - return this._upsertElement("notes", note, remove) + const track = this.tracks.find(t => t.id === note.trackId) + return this._upsertTrackElement(track, "notes", note, remove) } upsertNotes(notes, remove = false) { - return this._upsertElements("notes", notes, remove) + let res = this + for (const note of notes) + res = res.upsertNote(note, remove) + + return res + //return this._upsertElements("notes", notes, remove) + } + + + upsertTrack(track, remove = false) + { + return this._upsertTrack(track, remove) } @@ -150,8 +217,14 @@ export default class Project findById(id) { + for (const track of this.tracks.values()) + { + const elem = track.notes.findById(id) + if (elem) + return elem + } + return ( - this.notes.findById(id) || this.chords.findById(id) || this.keyChanges.findById(id) || this.meterChanges.findById(id) @@ -162,7 +235,7 @@ export default class Project update(elem) { let song = this.withChanges({}) - song.notes = song.notes.update(elem) + //song.notes = song.notes.update(elem) song.chords = song.chords.update(elem) song.keyChanges = song.keyChanges.update(elem) song.meterChanges = song.meterChanges.update(elem) @@ -173,7 +246,7 @@ export default class Project removeById(id) { let song = this.withChanges({}) - song.notes = song.notes.removeById(id) + //song.notes = song.notes.removeById(id) song.chords = song.chords.removeById(id) song.keyChanges = song.keyChanges.removeById(id) song.meterChanges = song.meterChanges.removeById(id) @@ -211,11 +284,29 @@ export default class Project } + static Track = class ProjectTrack + { + constructor() + { + this.id = -1 + this.name = "New Track" + this.notes = new ListOfRanges() + } + + + withChanges(obj) + { + return Object.assign(new ProjectTrack(), { id: this.id, name: this.name, notes: this.notes }, obj) + } + } + + static Note = class ProjectNote { - constructor(range, pitch) + constructor(trackId, range, pitch) { this.id = -1 + this.trackId = trackId this.range = range this.pitch = pitch } @@ -223,7 +314,7 @@ export default class Project withChanges(obj) { - return Object.assign(new ProjectNote(this.range, this.pitch), { id: this.id }, obj) + return Object.assign(new ProjectNote(this.trackId, this.range, this.pitch), { id: this.id }, obj) } } diff --git a/src/toolbox/App.js b/src/toolbox/App.js index 8d2ba0e..a56f0fa 100644 --- a/src/toolbox/App.js +++ b/src/toolbox/App.js @@ -6,6 +6,7 @@ import ToolboxPlayback from "./ToolboxPlayback.js" import ToolboxFile from "./ToolboxFile.js" import ToolboxEdit from "./ToolboxEdit.js" import ToolboxInput from "./ToolboxInput.js" +import ToolboxTracks from "./ToolboxTracks.js" import Ribbon from "./Ribbon.js" @@ -157,7 +158,7 @@ export default function App(props) state = Editor.reduce(state, { type: "init", project: Project.getDefault() }) state = Editor.reduce(state, { type: "trackAdd", kind: "markers" }) state = Editor.reduce(state, { type: "trackAdd", kind: "chords" }) - state = Editor.reduce(state, { type: "trackAdd", kind: "notes" }) + state = Editor.reduce(state, { type: "trackAdd", kind: "notes", trackId: state.project.tracks[0].id }) state = Editor.reduce(state, { type: "clearUndoStack" }) const urlData = @@ -191,6 +192,7 @@ export default function App(props) boxSizing: "border-box", display: "grid", gridTemplate: "150px 1fr / 1fr", + backgroundColor: "#eee", }}>
@@ -211,29 +213,35 @@ export default function App(props) { ToolboxInput({ state, dispatch }) } - {/* + +
+ - - */} + style={{ + gridRow: 1, + gridColumn: 2, + width: "100%", + height: "100%", + }}/>
- -
) } diff --git a/src/toolbox/ToolboxTracks.js b/src/toolbox/ToolboxTracks.js new file mode 100644 index 0000000..88e4695 --- /dev/null +++ b/src/toolbox/ToolboxTracks.js @@ -0,0 +1,124 @@ +import React from "react" +import Editor from "../editor/editor.js" + + + +export default function ToolboxTracks(props) +{ + const state = props.state + const dispatch = props.dispatch + + const time = Editor.insertionTime(props.state) + const cursorKeyCh = state.project.keyChanges.findActiveAt(time) + const key = cursorKeyCh ? cursorKeyCh.key : Editor.defaultKey() + + const onClickTrack = (id) => + { + dispatch({ type: "trackClear" }) + dispatch({ type: "trackAdd", kind: "markers" }) + dispatch({ type: "trackAdd", kind: "chords" }) + dispatch({ type: "trackAdd", kind: "notes", trackId: id }) + } + + return
+
+
+ + +
+ đŸ‘ī¸ +
+ +
+ 🧅 +
+ +
+ + { state.project.tracks.map(track => +
+ + +
+ đŸ‘ī¸ +
+ +
+ 🧅 +
+ +
+ )} +
+
+} \ No newline at end of file diff --git a/src/util/midi.js b/src/util/midi.js index 2dc22a2..141f392 100644 --- a/src/util/midi.js +++ b/src/util/midi.js @@ -141,7 +141,14 @@ export class MidiFile { let r = new BinaryReader(event.rawData) - if (event.metaType == 0x2f) + if (event.metaType == 0x03) + { + event.kind = "trackName" + event.description = "[FF 03] Track Name" + event.name = r.readAsciiLength(event.rawData.length) + } + + else if (event.metaType == 0x2f) { event.kind = "endOfTrack" event.description = "[FF 2F] End of Track" diff --git a/webpack/main.js b/webpack/main.js index 612e8bb..3db1954 100644 --- a/webpack/main.js +++ b/webpack/main.js @@ -4,12 +4,12 @@ * * @author Feross Aboukhadijeh * @license MIT - */function i(t,e){if(t===e)return 0;for(var r=t.length,n=e.length,i=0,o=Math.min(r,n);i=0;c--)if(l[c]!==f[c])return!1;for(c=l.length-1;c>=0;c--)if(s=l[c],!b(t[s],e[s],r,n))return!1;return!0}(t,e,r,n))}return r?t===e:t==e}function w(t){return"[object Arguments]"==Object.prototype.toString.call(t)}function _(t,e){if(!t||!e)return!1;if("[object RegExp]"==Object.prototype.toString.call(e))return e.test(t);try{if(t instanceof e)return!0}catch(t){}return!Error.isPrototypeOf(e)&&!0===e.call({},t)}function k(t,e,r,n){var i;if("function"!=typeof e)throw new TypeError('"block" argument must be a function');"string"==typeof r&&(n=r,r=null),i=function(t){var e;try{t()}catch(t){e=t}return e}(e),n=(r&&r.name?" ("+r.name+").":".")+(n?" "+n:"."),t&&!i&&v(i,r,"Missing expected exception"+n);var o="string"==typeof n,s=!t&&i&&!r;if((!t&&a.isError(i)&&o&&_(i,r)||s)&&v(i,r,"Got unwanted exception"+n),t&&i&&r&&!_(i,r)||!t&&i)throw i}d.AssertionError=function(t){this.name="AssertionError",this.actual=t.actual,this.expected=t.expected,this.operator=t.operator,t.message?(this.message=t.message,this.generatedMessage=!1):(this.message=function(t){return m(g(t.actual),128)+" "+t.operator+" "+m(g(t.expected),128)}(this),this.generatedMessage=!0);var e=t.stackStartFunction||v;if(Error.captureStackTrace)Error.captureStackTrace(this,e);else{var r=new Error;if(r.stack){var n=r.stack,i=p(e),o=n.indexOf("\n"+i);if(o>=0){var a=n.indexOf("\n",o+1);n=n.substring(a+1)}this.stack=n}}},a.inherits(d.AssertionError,Error),d.fail=v,d.ok=y,d.equal=function(t,e,r){t!=e&&v(t,e,r,"==",d.equal)},d.notEqual=function(t,e,r){t==e&&v(t,e,r,"!=",d.notEqual)},d.deepEqual=function(t,e,r){b(t,e,!1)||v(t,e,r,"deepEqual",d.deepEqual)},d.deepStrictEqual=function(t,e,r){b(t,e,!0)||v(t,e,r,"deepStrictEqual",d.deepStrictEqual)},d.notDeepEqual=function(t,e,r){b(t,e,!1)&&v(t,e,r,"notDeepEqual",d.notDeepEqual)},d.notDeepStrictEqual=function t(e,r,n){b(e,r,!0)&&v(e,r,n,"notDeepStrictEqual",t)},d.strictEqual=function(t,e,r){t!==e&&v(t,e,r,"===",d.strictEqual)},d.notStrictEqual=function(t,e,r){t===e&&v(t,e,r,"!==",d.notStrictEqual)},d.throws=function(t,e,r){k(!0,t,e,r)},d.doesNotThrow=function(t,e,r){k(!1,t,e,r)},d.ifError=function(t){if(t)throw t},d.strict=n((function t(e,r){e||v(e,!0,r,"==",t)}),d,{equal:d.strictEqual,deepEqual:d.deepStrictEqual,notEqual:d.notStrictEqual,notDeepEqual:d.notDeepStrictEqual}),d.strict.strict=d.strict;var x=Object.keys||function(t){var e=[];for(var r in t)s.call(t,r)&&e.push(r);return e}}).call(this,r(133))},function(t,e,r){var n=r(10);t.exports=function(t){if(!n(t))throw TypeError(String(t)+" is not an object");return t}},function(t,e){t.exports=function(t){try{return!!t()}catch(t){return!0}}},function(t,e){t.exports=function(t){if("function"!=typeof t)throw TypeError(String(t)+" is not a function");return t}},function(t,e){t.exports=!1},function(t,e,r){(function(e){var r=function(t){return t&&t.Math==Math&&t};t.exports=r("object"==typeof globalThis&&globalThis)||r("object"==typeof window&&window)||r("object"==typeof self&&self)||r("object"==typeof e&&e)||Function("return this")()}).call(this,r(133))},function(t,e,r){var n=r(3),i=r(109),o=r(12),a=r(20),s=r(47),u=r(71),c=function(t,e){this.stopped=t,this.result=e};(t.exports=function(t,e,r,l,f){var d,h,p,m,g,v,y,b=a(e,r,l?2:1);if(f)d=t;else{if("function"!=typeof(h=s(t)))throw TypeError("Target is not iterable");if(i(h)){for(p=0,m=o(t.length);m>p;p++)if((g=l?b(n(y=t[p])[0],y[1]):b(t[p]))&&g instanceof c)return g;return new c(!1)}d=h.call(t)}for(v=d.next;!(y=v.call(d)).done;)if("object"==typeof(g=u(d,b,y.value,l))&&g&&g instanceof c)return g;return new c(!1)}).stop=function(t){return new c(!0,t)}},function(t,e,r){var n=r(7),i=r(80),o=r(17),a=r(63),s=r(107),u=r(138),c=i("wks"),l=n.Symbol,f=u?l:l&&l.withoutSetter||a;t.exports=function(t){return o(c,t)||(s&&o(l,t)?c[t]=l[t]:c[t]=f("Symbol."+t)),c[t]}},function(t,e){t.exports=function(t){return"object"==typeof t?null!==t:"function"==typeof t}},function(t,e,r){var n=r(4);t.exports=!n((function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a}))},function(t,e,r){var n=r(30),i=Math.min;t.exports=function(t){return t>0?i(n(t),9007199254740991):0}},function(t,e,r){"use strict";var n,i=r(11),o=r(7),a=r(10),s=r(17),u=r(70),c=r(18),l=r(26),f=r(14).f,d=r(27),h=r(52),p=r(9),m=r(63),g=o.DataView,v=g&&g.prototype,y=o.Int8Array,b=y&&y.prototype,w=o.Uint8ClampedArray,_=w&&w.prototype,k=y&&d(y),x=b&&d(b),S=Object.prototype,E=S.isPrototypeOf,C=p("toStringTag"),T=m("TYPED_ARRAY_TAG"),A=!(!o.ArrayBuffer||!g),I=A&&!!h&&"Opera"!==u(o.opera),M=!1,P={Int8Array:1,Uint8Array:1,Uint8ClampedArray:1,Int16Array:2,Uint16Array:2,Int32Array:4,Uint32Array:4,Float32Array:4,Float64Array:8},O=function(t){return a(t)&&s(P,u(t))};for(n in P)o[n]||(I=!1);if((!I||"function"!=typeof k||k===Function.prototype)&&(k=function(){throw TypeError("Incorrect invocation")},I))for(n in P)o[n]&&h(o[n],k);if((!I||!x||x===S)&&(x=k.prototype,I))for(n in P)o[n]&&h(o[n].prototype,x);if(I&&d(_)!==x&&h(_,x),i&&!s(x,C))for(n in M=!0,f(x,C,{get:function(){return a(this)?this[T]:void 0}}),P)o[n]&&c(o[n],T,n);A&&h&&d(v)!==S&&h(v,S),t.exports={NATIVE_ARRAY_BUFFER:A,NATIVE_ARRAY_BUFFER_VIEWS:I,TYPED_ARRAY_TAG:M&&T,aTypedArray:function(t){if(O(t))return t;throw TypeError("Target is not a typed array")},aTypedArrayConstructor:function(t){if(h){if(E.call(k,t))return t}else for(var e in P)if(s(P,n)){var r=o[e];if(r&&(t===r||E.call(r,t)))return t}throw TypeError("Target is not a typed array constructor")},exportTypedArrayMethod:function(t,e,r){if(i){if(r)for(var n in P){var a=o[n];a&&s(a.prototype,t)&&delete a.prototype[t]}x[t]&&!r||l(x,t,r?e:I&&b[t]||e)}},exportTypedArrayStaticMethod:function(t,e,r){var n,a;if(i){if(h){if(r)for(n in P)(a=o[n])&&s(a,t)&&delete a[t];if(k[t]&&!r)return;try{return l(k,t,r?e:I&&y[t]||e)}catch(t){}}for(n in P)!(a=o[n])||a[t]&&!r||l(a,t,e)}},isView:function(t){var e=u(t);return"DataView"===e||s(P,e)},isTypedArray:O,TypedArray:k,TypedArrayPrototype:x}},function(t,e,r){var n=r(11),i=r(134),o=r(3),a=r(34),s=Object.defineProperty;e.f=n?s:function(t,e,r){if(o(t),e=a(e,!0),o(r),i)try{return s(t,e,r)}catch(t){}if("get"in r||"set"in r)throw TypeError("Accessors not supported");return"value"in r&&(t[e]=r.value),t}},function(t,e,r){var n=r(36),i=r(7),o=function(t){return"function"==typeof t?t:void 0};t.exports=function(t,e){return arguments.length<2?o(n[t])||o(i[t]):n[t]&&n[t][e]||i[t]&&i[t][e]}},function(t,e,r){var n=r(23);t.exports=function(t){return Object(n(t))}},function(t,e){var r={}.hasOwnProperty;t.exports=function(t,e){return r.call(t,e)}},function(t,e,r){var n=r(11),i=r(14),o=r(40);t.exports=n?function(t,e,r){return i.f(t,e,o(1,r))}:function(t,e,r){return t[e]=r,t}},function(t,e,r){var n,i,o,a=r(135),s=r(7),u=r(10),c=r(18),l=r(17),f=r(79),d=r(64),h=s.WeakMap;if(a){var p=new h,m=p.get,g=p.has,v=p.set;n=function(t,e){return v.call(p,t,e),e},i=function(t){return m.call(p,t)||{}},o=function(t){return g.call(p,t)}}else{var y=f("state");d[y]=!0,n=function(t,e){return c(t,y,e),e},i=function(t){return l(t,y)?t[y]:{}},o=function(t){return l(t,y)}}t.exports={set:n,get:i,has:o,enforce:function(t){return o(t)?i(t):n(t,{})},getterFor:function(t){return function(e){var r;if(!u(e)||(r=i(e)).type!==t)throw TypeError("Incompatible receiver, "+t+" required");return r}}}},function(t,e,r){var n=r(5);t.exports=function(t,e,r){if(n(t),void 0===e)return t;switch(r){case 0:return function(){return t.call(e)};case 1:return function(r){return t.call(e,r)};case 2:return function(r,n){return t.call(e,r,n)};case 3:return function(r,n,i){return t.call(e,r,n,i)}}return function(){return t.apply(e,arguments)}}},function(t,e,r){var n=r(36),i=r(17),o=r(141),a=r(14).f;t.exports=function(t){var e=n.Symbol||(n.Symbol={});i(e,t)||a(e,t,{value:o.f(t)})}},function(t,e,r){var n=r(3),i=r(5),o=r(9)("species");t.exports=function(t,e){var r,a=n(t).constructor;return void 0===a||null==(r=n(a)[o])?e:i(r)}},function(t,e){t.exports=function(t){if(null==t)throw TypeError("Can't call method on "+t);return t}},function(t,e,r){var n=r(20),i=r(62),o=r(16),a=r(12),s=r(67),u=[].push,c=function(t){var e=1==t,r=2==t,c=3==t,l=4==t,f=6==t,d=5==t||f;return function(h,p,m,g){for(var v,y,b=o(h),w=i(b),_=n(p,m,3),k=a(w.length),x=0,S=g||s,E=e?S(h,k):r?S(h,0):void 0;k>x;x++)if((d||x in w)&&(y=_(v=w[x],x,b),t))if(e)E[x]=y;else if(y)switch(t){case 3:return!0;case 5:return v;case 6:return x;case 2:u.call(E,v)}else if(l)return!1;return f?-1:c||l?l:E}};t.exports={forEach:c(0),map:c(1),filter:c(2),some:c(3),every:c(4),find:c(5),findIndex:c(6)}},function(t,e,r){var n=r(11),i=r(78),o=r(40),a=r(29),s=r(34),u=r(17),c=r(134),l=Object.getOwnPropertyDescriptor;e.f=n?l:function(t,e){if(t=a(t),e=s(e,!0),c)try{return l(t,e)}catch(t){}if(u(t,e))return o(!i.f.call(t,e),t[e])}},function(t,e,r){var n=r(7),i=r(18),o=r(17),a=r(101),s=r(102),u=r(19),c=u.get,l=u.enforce,f=String(String).split("String");(t.exports=function(t,e,r,s){var u=!!s&&!!s.unsafe,c=!!s&&!!s.enumerable,d=!!s&&!!s.noTargetGet;"function"==typeof r&&("string"!=typeof e||o(r,"name")||i(r,"name",e),l(r).source=f.join("string"==typeof e?e:"")),t!==n?(u?!d&&t[e]&&(c=!0):delete t[e],c?t[e]=r:i(t,e,r)):c?t[e]=r:a(e,r)})(Function.prototype,"toString",(function(){return"function"==typeof this&&c(this).source||s(this)}))},function(t,e,r){var n=r(17),i=r(16),o=r(79),a=r(111),s=o("IE_PROTO"),u=Object.prototype;t.exports=a?Object.getPrototypeOf:function(t){return t=i(t),n(t,s)?t[s]:"function"==typeof t.constructor&&t instanceof t.constructor?t.constructor.prototype:t instanceof Object?u:null}},function(t,e,r){var n,i=r(3),o=r(108),a=r(105),s=r(64),u=r(139),c=r(100),l=r(79),f=l("IE_PROTO"),d=function(){},h=function(t){return"