abcjs 6.2.0 → 6.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +1 -1
- package/README.md +3 -0
- package/RELEASE.md +31 -0
- package/dist/abcjs-basic-min.js +2 -2
- package/dist/abcjs-basic-min.js.LICENSE +1 -1
- package/dist/abcjs-basic.js +89 -22
- package/dist/abcjs-basic.js.map +1 -1
- package/dist/abcjs-plugin-min.js +2 -2
- package/dist/abcjs-plugin-min.js.LICENSE +1 -1
- package/index.js +1 -1
- package/license.js +1 -1
- package/package.json +1 -1
- package/plugin.js +1 -1
- package/src/api/abc_tablatures.js +2 -0
- package/src/api/abc_timing_callbacks.js +1 -1
- package/src/data/abc_tune.js +1 -1
- package/src/synth/create-synth.js +30 -4
- package/src/synth/load-note.js +1 -1
- package/src/synth/place-note.js +10 -2
- package/src/tablatures/instruments/string-patterns.js +1 -1
- package/src/tablatures/tab-renderer.js +1 -0
- package/src/write/draw/print-line.js +19 -0
- package/src/write/draw/print-stem.js +18 -0
- package/src/write/engraver-controller.js +5 -3
- package/src/write/interactive/selection.js +7 -4
- package/src/write/renderer.js +1 -0
- package/src/write/svg.js +10 -0
- package/test.js +1 -1
- package/types/index.d.ts +2 -2
- package/version.js +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**!
|
|
2
|
-
Copyright (c) 2009-
|
|
2
|
+
Copyright (c) 2009-2023 Paul Rosen and Gregory Dyke
|
|
3
3
|
|
|
4
4
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
5
|
of this software and associated documentation files (the "Software"), to deal
|
package/dist/abcjs-basic.js
CHANGED
|
@@ -18,7 +18,7 @@ return /******/ (function() { // webpackBootstrap
|
|
|
18
18
|
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
|
|
19
19
|
|
|
20
20
|
/**!
|
|
21
|
-
Copyright (c) 2009-
|
|
21
|
+
Copyright (c) 2009-2023 Paul Rosen and Gregory Dyke
|
|
22
22
|
|
|
23
23
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
24
24
|
of this software and associated documentation files (the "Software"), to deal
|
|
@@ -211,6 +211,8 @@ var GuitarTablature = __webpack_require__(/*! ../tablatures/instruments/guitar/t
|
|
|
211
211
|
// Existing tab classes
|
|
212
212
|
var pluginTab = {
|
|
213
213
|
'violin': 'ViolinTab',
|
|
214
|
+
'fiddle': 'ViolinTab',
|
|
215
|
+
'mandolin': 'ViolinTab',
|
|
214
216
|
'guitar': 'GuitarTab'
|
|
215
217
|
};
|
|
216
218
|
var abcTablatures = {
|
|
@@ -352,7 +354,7 @@ var TimingCallbacks = function TimingCallbacks(target, params) {
|
|
|
352
354
|
self.joggerTimer = null;
|
|
353
355
|
self.replaceTarget = function (newTarget) {
|
|
354
356
|
self.noteTimings = newTarget.setTiming(self.qpm, self.extraMeasuresAtBeginning);
|
|
355
|
-
if (newTarget.noteTimings.length === 0) newTarget.setTiming(0, 0);
|
|
357
|
+
if (newTarget.noteTimings.length === 0) self.noteTimings = newTarget.setTiming(0, 0);
|
|
356
358
|
if (self.lineEndCallback) {
|
|
357
359
|
self.lineEndTimings = getLineEndTimings(newTarget.noteTimings, self.lineEndAnticipation);
|
|
358
360
|
}
|
|
@@ -1797,7 +1799,7 @@ var Tune = function Tune() {
|
|
|
1797
1799
|
if (!this.engraver || !this.engraver.staffgroups) {
|
|
1798
1800
|
console.log("setTiming cannot be called before the tune is drawn.");
|
|
1799
1801
|
this.noteTimings = [];
|
|
1800
|
-
return;
|
|
1802
|
+
return this.noteTimings;
|
|
1801
1803
|
}
|
|
1802
1804
|
var tempo = this.metaText ? this.metaText.tempo : null;
|
|
1803
1805
|
var naturalBpm = this.getBpm(tempo);
|
|
@@ -13881,6 +13883,8 @@ function CreateSynth() {
|
|
|
13881
13883
|
});
|
|
13882
13884
|
});
|
|
13883
13885
|
});
|
|
13886
|
+
if (self.debugCallback) self.debugCallback("notes " + JSON.stringify(notes));
|
|
13887
|
+
|
|
13884
13888
|
// If there are lots of notes, load them in batches
|
|
13885
13889
|
var batches = [];
|
|
13886
13890
|
var CHUNK = 256;
|
|
@@ -13895,8 +13899,10 @@ function CreateSynth() {
|
|
|
13895
13899
|
};
|
|
13896
13900
|
var index = 0;
|
|
13897
13901
|
var next = function next() {
|
|
13902
|
+
if (self.debugCallback) self.debugCallback("loadBatch idx=" + index + " len=" + batches.length);
|
|
13898
13903
|
if (index < batches.length) {
|
|
13899
13904
|
self._loadBatch(batches[index], self.soundFontUrl, startTime).then(function (data) {
|
|
13905
|
+
if (self.debugCallback) self.debugCallback("loadBatch then");
|
|
13900
13906
|
startTime = activeAudioContext().currentTime;
|
|
13901
13907
|
if (data) {
|
|
13902
13908
|
if (data.error) results.error = results.error.concat(data.error);
|
|
@@ -13906,6 +13912,7 @@ function CreateSynth() {
|
|
|
13906
13912
|
next();
|
|
13907
13913
|
}, reject);
|
|
13908
13914
|
} else {
|
|
13915
|
+
if (self.debugCallback) self.debugCallback("resolve init");
|
|
13909
13916
|
resolve(results);
|
|
13910
13917
|
}
|
|
13911
13918
|
};
|
|
@@ -13916,6 +13923,7 @@ function CreateSynth() {
|
|
|
13916
13923
|
// This is called recursively to see if the sounds have loaded. The "delay" parameter is how long it has been since the original call.
|
|
13917
13924
|
var promises = [];
|
|
13918
13925
|
batch.forEach(function (item) {
|
|
13926
|
+
if (self.debugCallback) self.debugCallback("getNote " + item.instrument + ':' + item.note);
|
|
13919
13927
|
promises.push(getNote(soundFontUrl, item.instrument, item.note, activeAudioContext()));
|
|
13920
13928
|
});
|
|
13921
13929
|
return Promise.all(promises).then(function (response) {
|
|
@@ -13930,6 +13938,7 @@ function CreateSynth() {
|
|
|
13930
13938
|
if (oneResponse.status === "loaded") loaded.push(which);else if (oneResponse.status === "pending") pending.push(which);else if (oneResponse.status === "cached") cached.push(which);else error.push(which + ' ' + oneResponse.message);
|
|
13931
13939
|
}
|
|
13932
13940
|
if (pending.length > 0) {
|
|
13941
|
+
if (self.debugCallback) self.debugCallback("pending " + JSON.stringify(pending));
|
|
13933
13942
|
// There was probably a second call for notes before the first one finished, so just retry a few times to see if they stop being pending.
|
|
13934
13943
|
// Retry quickly at first so that there isn't an unnecessary delay, but increase the delay each time.
|
|
13935
13944
|
if (!delay) delay = 50;else delay = delay * 2;
|
|
@@ -13944,6 +13953,7 @@ function CreateSynth() {
|
|
|
13944
13953
|
note: which[1]
|
|
13945
13954
|
});
|
|
13946
13955
|
}
|
|
13956
|
+
if (self.debugCallback) self.debugCallback("retry " + JSON.stringify(newBatch));
|
|
13947
13957
|
self._loadBatch(newBatch, soundFontUrl, startTime, delay).then(function (response) {
|
|
13948
13958
|
resolve(response);
|
|
13949
13959
|
})["catch"](function (error) {
|
|
@@ -13956,14 +13966,20 @@ function CreateSynth() {
|
|
|
13956
13966
|
for (var j = 0; j < batch.length; j++) {
|
|
13957
13967
|
list.push(batch[j].instrument + '/' + batch[j].note);
|
|
13958
13968
|
}
|
|
13969
|
+
if (self.debugCallback) self.debugCallback("loadBatch timeout");
|
|
13959
13970
|
return Promise.reject(new Error("timeout attempting to load: " + list.join(", ")));
|
|
13960
13971
|
}
|
|
13961
|
-
} else
|
|
13962
|
-
|
|
13963
|
-
|
|
13964
|
-
|
|
13965
|
-
|
|
13966
|
-
|
|
13972
|
+
} else {
|
|
13973
|
+
if (self.debugCallback) self.debugCallback("loadBatch resolve");
|
|
13974
|
+
return Promise.resolve({
|
|
13975
|
+
loaded: loaded,
|
|
13976
|
+
cached: cached,
|
|
13977
|
+
error: error
|
|
13978
|
+
});
|
|
13979
|
+
}
|
|
13980
|
+
})["catch"](function (error) {
|
|
13981
|
+
if (self.debugCallback) self.debugCallback("loadBatch catch " + error.message);
|
|
13982
|
+
});
|
|
13967
13983
|
};
|
|
13968
13984
|
self.prime = function () {
|
|
13969
13985
|
// At this point all of the notes are loaded. This function writes them into the output buffer.
|
|
@@ -14002,6 +14018,7 @@ function CreateSynth() {
|
|
|
14002
14018
|
var panDistance = panDistances && panDistances.length > trackNumber ? panDistances[trackNumber] : 0;
|
|
14003
14019
|
noteMap.forEach(function (note) {
|
|
14004
14020
|
var key = note.instrument + ':' + note.pitch + ':' + note.volume + ':' + Math.round((note.end - note.start) * 1000) / 1000 + ':' + panDistance + ':' + tempoMultiplier + ':' + (note.cents ? note.cents : 0);
|
|
14021
|
+
if (self.debugCallback) self.debugCallback("noteMapTrack " + key);
|
|
14005
14022
|
if (!uniqueSounds[key]) uniqueSounds[key] = [];
|
|
14006
14023
|
uniqueSounds[key].push(note.start);
|
|
14007
14024
|
});
|
|
@@ -14023,7 +14040,7 @@ function CreateSynth() {
|
|
|
14023
14040
|
tempoMultiplier: parseFloat(parts[5]),
|
|
14024
14041
|
cents: cents
|
|
14025
14042
|
};
|
|
14026
|
-
allPromises.push(placeNote(audioBuffer, activeAudioContext().sampleRate, parts, uniqueSounds[k], self.soundFontVolumeMultiplier, self.programOffsets[parts.instrument], fadeTimeSec, self.noteEnd / 1000));
|
|
14043
|
+
allPromises.push(placeNote(audioBuffer, activeAudioContext().sampleRate, parts, uniqueSounds[k], self.soundFontVolumeMultiplier, self.programOffsets[parts.instrument], fadeTimeSec, self.noteEnd / 1000, self.debugCallback));
|
|
14027
14044
|
}
|
|
14028
14045
|
self.audioBuffers = [audioBuffer];
|
|
14029
14046
|
if (self.debugCallback) {
|
|
@@ -14462,7 +14479,7 @@ var getNote = function getNote(url, instrument, name, audioContext) {
|
|
|
14462
14479
|
xhr.responseType = "arraybuffer";
|
|
14463
14480
|
xhr.onload = function () {
|
|
14464
14481
|
if (xhr.status !== 200) {
|
|
14465
|
-
reject(Error("Can't load sound at " + noteUrl));
|
|
14482
|
+
reject(Error("Can't load sound at " + noteUrl + ' status=' + xhr.status));
|
|
14466
14483
|
return;
|
|
14467
14484
|
}
|
|
14468
14485
|
var noteDecoded = function noteDecoded(audioBuffer) {
|
|
@@ -14756,7 +14773,7 @@ module.exports = pitchesToPerc;
|
|
|
14756
14773
|
var soundsCache = __webpack_require__(/*! ./sounds-cache */ "./src/synth/sounds-cache.js");
|
|
14757
14774
|
var pitchToNoteName = __webpack_require__(/*! ./pitch-to-note-name */ "./src/synth/pitch-to-note-name.js");
|
|
14758
14775
|
var centsToFactor = __webpack_require__(/*! ./cents-to-factor */ "./src/synth/cents-to-factor.js");
|
|
14759
|
-
function placeNote(outputAudioBuffer, sampleRate, sound, startArray, volumeMultiplier, ofsMs, fadeTimeSec, noteEndSec) {
|
|
14776
|
+
function placeNote(outputAudioBuffer, sampleRate, sound, startArray, volumeMultiplier, ofsMs, fadeTimeSec, noteEndSec, debugCallback) {
|
|
14760
14777
|
// sound contains { instrument, pitch, volume, len, pan, tempoMultiplier
|
|
14761
14778
|
// len is in whole notes. Multiply by tempoMultiplier to get seconds.
|
|
14762
14779
|
// ofsMs is an offset to subtract from the note to line up programs that have different length onsets.
|
|
@@ -14770,6 +14787,7 @@ function placeNote(outputAudioBuffer, sampleRate, sound, startArray, volumeMulti
|
|
|
14770
14787
|
var noteBufferPromise = soundsCache[sound.instrument][noteName];
|
|
14771
14788
|
if (!noteBufferPromise) {
|
|
14772
14789
|
// if the note isn't present then just skip it - it will leave a blank spot in the audio.
|
|
14790
|
+
if (debugCallback) debugCallback('placeNote skipped: ' + sound.instrument + ':' + noteName);
|
|
14773
14791
|
return Promise.resolve();
|
|
14774
14792
|
}
|
|
14775
14793
|
return noteBufferPromise.then(function (response) {
|
|
@@ -14825,13 +14843,17 @@ function placeNote(outputAudioBuffer, sampleRate, sound, startArray, volumeMulti
|
|
|
14825
14843
|
copyToChannel(outputAudioBuffer, e.renderedBuffer, start);
|
|
14826
14844
|
}
|
|
14827
14845
|
}
|
|
14846
|
+
if (debugCallback) debugCallback('placeNote: ' + sound.instrument + ':' + noteName);
|
|
14828
14847
|
fnResolve();
|
|
14829
14848
|
};
|
|
14830
14849
|
offlineCtx.startRendering();
|
|
14831
14850
|
return new Promise(function (resolve) {
|
|
14832
14851
|
fnResolve = resolve;
|
|
14833
14852
|
});
|
|
14834
|
-
})["catch"](function () {
|
|
14853
|
+
})["catch"](function (error) {
|
|
14854
|
+
if (debugCallback) debugCallback('placeNote catch: ' + error.message);
|
|
14855
|
+
return Promise.resolve();
|
|
14856
|
+
});
|
|
14835
14857
|
}
|
|
14836
14858
|
var copyToChannel = function copyToChannel(toBuffer, fromBuffer, start) {
|
|
14837
14859
|
for (var ch = 0; ch < 2; ch++) {
|
|
@@ -15618,7 +15640,7 @@ function StringPatterns(plugin) {
|
|
|
15618
15640
|
this.measureAccidentals = {};
|
|
15619
15641
|
this.capo = 0;
|
|
15620
15642
|
if (capo) {
|
|
15621
|
-
this.capo = capo;
|
|
15643
|
+
this.capo = parseInt(capo, 10);
|
|
15622
15644
|
}
|
|
15623
15645
|
this.transpose = plugin.transpose ? plugin.transpose : 0;
|
|
15624
15646
|
this.tuning = tuning;
|
|
@@ -16612,6 +16634,7 @@ TabRenderer.prototype.doLayout = function () {
|
|
|
16612
16634
|
this.tabStaff.voices = [];
|
|
16613
16635
|
for (var ii = 0; ii < nbVoices; ii++) {
|
|
16614
16636
|
var tabVoice = new VoiceElement(0, 0);
|
|
16637
|
+
if (ii > 0) tabVoice.duplicate = true;
|
|
16615
16638
|
var nameHeight = buildTabName(this, tabVoice) / spacing.STEP;
|
|
16616
16639
|
nameHeight = Math.max(nameHeight, 1); // If there is no label for the tab line, then there needs to be a little padding
|
|
16617
16640
|
staffGroup.staffs[this.staffIndex].top += nameHeight;
|
|
@@ -21545,6 +21568,21 @@ function printLine(renderer, x1, x2, y, klass, name, dy) {
|
|
|
21545
21568
|
x2 = roundNumber(x2);
|
|
21546
21569
|
var y1 = roundNumber(y - dy);
|
|
21547
21570
|
var y2 = roundNumber(y + dy);
|
|
21571
|
+
// TODO-PER: This fixes a firefox bug where it isn't displayed
|
|
21572
|
+
if (renderer.firefox112) {
|
|
21573
|
+
y += dy / 2; // Because the y coordinate is the edge of where the line goes but the width widens from the middle.
|
|
21574
|
+
var attr = {
|
|
21575
|
+
x1: x1,
|
|
21576
|
+
x2: x2,
|
|
21577
|
+
y1: y,
|
|
21578
|
+
y2: y,
|
|
21579
|
+
stroke: renderer.foregroundColor,
|
|
21580
|
+
'stroke-width': Math.abs(dy * 2)
|
|
21581
|
+
};
|
|
21582
|
+
if (klass) attr['class'] = klass;
|
|
21583
|
+
if (name) attr['data-name'] = name;
|
|
21584
|
+
return renderer.paper.lineToBack(attr);
|
|
21585
|
+
}
|
|
21548
21586
|
var pathString = sprintf("M %f %f L %f %f L %f %f L %f %f z", x1, y1, x2, y1, x2, y2, x1, y2);
|
|
21549
21587
|
var options = {
|
|
21550
21588
|
path: pathString,
|
|
@@ -21594,6 +21632,21 @@ function printStem(renderer, x, dx, y1, y2, klass, name) {
|
|
|
21594
21632
|
}
|
|
21595
21633
|
x = roundNumber(x);
|
|
21596
21634
|
var x2 = roundNumber(x + dx);
|
|
21635
|
+
// TODO-PER: This fixes a firefox bug where it isn't displayed
|
|
21636
|
+
if (renderer.firefox112) {
|
|
21637
|
+
x += dx / 2; // Because the x coordinate is the edge of where the line goes but the width widens from the middle.
|
|
21638
|
+
var attr = {
|
|
21639
|
+
x1: x,
|
|
21640
|
+
x2: x,
|
|
21641
|
+
y1: y1,
|
|
21642
|
+
y2: y2,
|
|
21643
|
+
stroke: renderer.foregroundColor,
|
|
21644
|
+
'stroke-width': Math.abs(dx)
|
|
21645
|
+
};
|
|
21646
|
+
if (klass) attr['class'] = klass;
|
|
21647
|
+
if (name) attr['data-name'] = name;
|
|
21648
|
+
return renderer.paper.lineToBack(attr);
|
|
21649
|
+
}
|
|
21597
21650
|
var pathArray = [["M", x, y1], ["L", x, y2], ["L", x2, y2], ["L", x2, y1], ["z"]];
|
|
21598
21651
|
var attr = {
|
|
21599
21652
|
path: ""
|
|
@@ -23108,13 +23161,13 @@ EngraverController.prototype.engraveTune = function (abcTune, tuneNumber, lineOf
|
|
|
23108
23161
|
this.selectables = ret.selectables;
|
|
23109
23162
|
if (this.oneSvgPerLine) {
|
|
23110
23163
|
var div = this.renderer.paper.svg.parentNode;
|
|
23111
|
-
this.svgs = splitSvgIntoLines(div, abcTune.metaText.title, this.responsive);
|
|
23164
|
+
this.svgs = splitSvgIntoLines(this.renderer, div, abcTune.metaText.title, this.responsive);
|
|
23112
23165
|
} else {
|
|
23113
23166
|
this.svgs = [this.renderer.paper.svg];
|
|
23114
23167
|
}
|
|
23115
23168
|
setupSelection(this, this.svgs);
|
|
23116
23169
|
};
|
|
23117
|
-
function splitSvgIntoLines(output, title, responsive) {
|
|
23170
|
+
function splitSvgIntoLines(renderer, output, title, responsive) {
|
|
23118
23171
|
// Each line is a top level <g> in the svg. To split it into separate
|
|
23119
23172
|
// svgs iterate through each of those and put them in a new svg. Since
|
|
23120
23173
|
// they are placed absolutely, the viewBox needs to be manipulated to
|
|
@@ -23144,7 +23197,9 @@ function splitSvgIntoLines(output, title, responsive) {
|
|
|
23144
23197
|
svg.setAttribute("aria-label", fullTitle);
|
|
23145
23198
|
if (responsive !== 'resize') svg.setAttribute("height", height);
|
|
23146
23199
|
if (responsive === 'resize') svg.style.position = '';
|
|
23147
|
-
|
|
23200
|
+
// TODO-PER: Hack! Not sure why this is needed.
|
|
23201
|
+
var viewBoxHeight = renderer.firefox112 ? height + 1 : height;
|
|
23202
|
+
svg.setAttribute("viewBox", "0 " + nextTop + " " + width + " " + viewBoxHeight);
|
|
23148
23203
|
svg.appendChild(style.cloneNode(true));
|
|
23149
23204
|
var titleEl = document.createElement("title");
|
|
23150
23205
|
titleEl.innerText = fullTitle;
|
|
@@ -23806,10 +23861,12 @@ function notifySelect(target, dragStep, dragMax, dragIndex, ev) {
|
|
|
23806
23861
|
while (parent && parent.dataset && !parent.dataset.index && parent.tagName.toLowerCase() !== 'svg') {
|
|
23807
23862
|
parent = parent.parentNode;
|
|
23808
23863
|
}
|
|
23809
|
-
|
|
23810
|
-
|
|
23811
|
-
|
|
23812
|
-
|
|
23864
|
+
if (parent && parent.dataset) {
|
|
23865
|
+
analysis.name = parent.dataset.name;
|
|
23866
|
+
analysis.clickedName = closest.dataset.name;
|
|
23867
|
+
analysis.parentClasses = parent.classList;
|
|
23868
|
+
}
|
|
23869
|
+
if (closest && closest.classList) analysis.clickedClasses = closest.classList;
|
|
23813
23870
|
analysis.selectableElement = target.svgEl;
|
|
23814
23871
|
for (var i = 0; i < this.listeners.length; i++) {
|
|
23815
23872
|
this.listeners[i](target.absEl.abcelem, target.absEl.tuneNumber, classes.join(' '), analysis, {
|
|
@@ -24992,6 +25049,7 @@ var Renderer = function Renderer(paper) {
|
|
|
24992
25049
|
this.space = 3 * spacing.SPACE;
|
|
24993
25050
|
this.padding = {}; // renderer's padding is managed by the controller
|
|
24994
25051
|
this.reset();
|
|
25052
|
+
this.firefox112 = navigator.userAgent.indexOf('Firefox/112.0') >= 0;
|
|
24995
25053
|
};
|
|
24996
25054
|
Renderer.prototype.reset = function () {
|
|
24997
25055
|
this.paper.clear();
|
|
@@ -25444,6 +25502,15 @@ Svg.prototype.pathToBack = function (attr) {
|
|
|
25444
25502
|
this.prepend(el);
|
|
25445
25503
|
return el;
|
|
25446
25504
|
};
|
|
25505
|
+
Svg.prototype.lineToBack = function (attr) {
|
|
25506
|
+
var el = document.createElementNS(svgNS, 'line');
|
|
25507
|
+
var keys = Object.keys(attr);
|
|
25508
|
+
for (var i = 0; i < keys.length; i++) {
|
|
25509
|
+
el.setAttribute(keys[i], attr[keys[i]]);
|
|
25510
|
+
}
|
|
25511
|
+
this.prepend(el);
|
|
25512
|
+
return el;
|
|
25513
|
+
};
|
|
25447
25514
|
Svg.prototype.append = function (el) {
|
|
25448
25515
|
if (this.currentGroup.length > 0) this.currentGroup[0].appendChild(el);else this.svg.appendChild(el);
|
|
25449
25516
|
};
|
|
@@ -25479,7 +25546,7 @@ module.exports = Svg;
|
|
|
25479
25546
|
\********************/
|
|
25480
25547
|
/***/ (function(module) {
|
|
25481
25548
|
|
|
25482
|
-
var version = '6.2.
|
|
25549
|
+
var version = '6.2.2';
|
|
25483
25550
|
module.exports = version;
|
|
25484
25551
|
|
|
25485
25552
|
/***/ })
|