danoniplus 40.7.2 → 41.0.1

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.
@@ -37,7 +37,7 @@ a:hover { color:#FF9900; text-decoration: underline; }
37
37
  <body>
38
38
  <table><tr><td>
39
39
  <p style="text-align:center;">
40
- <span style="font-size:32px;">Preview</span>
40
+ <span id="webMusicTitle">Preview</span>
41
41
  </p>
42
42
  <hr>
43
43
 
package/js/danoni_main.js CHANGED
@@ -4,12 +4,12 @@
4
4
  *
5
5
  * Source by tickle
6
6
  * Created : 2018/10/08
7
- * Revised : 2025/04/12
7
+ * Revised : 2025/04/16
8
8
  *
9
9
  * https://github.com/cwtickle/danoniplus
10
10
  */
11
- const g_version = `Ver 40.7.2`;
12
- const g_revisedDate = `2025/04/12`;
11
+ const g_version = `Ver 41.0.1`;
12
+ const g_revisedDate = `2025/04/16`;
13
13
 
14
14
  // カスタム用バージョン (danoni_custom.js 等で指定可)
15
15
  let g_localVersion = ``;
@@ -234,6 +234,7 @@ let g_langStorage = {};
234
234
  // ローカルストレージ設定 (作品別)
235
235
  let g_localStorage;
236
236
  let g_localStorageUrl;
237
+ let g_localStorageUrlOrg;
237
238
  let g_localStorageMgt;
238
239
 
239
240
  // ローカルストレージ設定 (ドメイン・キー別)
@@ -2081,7 +2082,7 @@ const makeSpriteData = (_data, _calcFrame = _frame => _frame) => {
2081
2082
  [spriteData[tmpFrame], dataCnts] =
2082
2083
  checkDuplicatedObjects(spriteData[tmpFrame]);
2083
2084
 
2084
- const emptyPatterns = [``, `[loop]`, `[jump]`];
2085
+ const emptyPatterns = [`[loop]`, `[jump]`];
2085
2086
  const colorObjFlg = tmpSpriteData[2]?.startsWith(`[c]`) || false;
2086
2087
  const spriteFrameData = spriteData[tmpFrame][dataCnts] = {
2087
2088
  depth: tmpDepth,
@@ -2103,8 +2104,10 @@ const makeSpriteData = (_data, _calcFrame = _frame => _frame) => {
2103
2104
  spriteFrameData.colorObjInfo.animationFillMode = tmpObj.animationFillMode;
2104
2105
  }
2105
2106
 
2107
+ } else if (tmpObj.path === ``) {
2108
+ spriteFrameData.command = ``;
2106
2109
  } else if (emptyPatterns.includes(tmpObj.path)) {
2107
- // ループ、フレームジャンプ、空の場合の処理
2110
+ // ループ、フレームジャンプの場合の処理
2108
2111
  spriteFrameData.command = tmpObj.path;
2109
2112
  spriteFrameData.jumpFrame = tmpObj.class;
2110
2113
  spriteFrameData.maxLoop = tmpObj.left;
@@ -2452,8 +2455,8 @@ const initialControl = async () => {
2452
2455
 
2453
2456
  // 共通設定ファイルの指定
2454
2457
  let tmpSettingType = g_rootObj.settingType ?? ``;
2455
- if (g_remoteFlg && !tmpSettingType.includes(`(..)`)) {
2456
- tmpSettingType = `(..)../js/${tmpSettingType}`;
2458
+ if (g_remoteFlg && !tmpSettingType.includes(C_MRK_CURRENT_DIRECTORY)) {
2459
+ tmpSettingType = `${C_MRK_CURRENT_DIRECTORY}../js/${tmpSettingType}`;
2457
2460
  };
2458
2461
  let [settingType, settingRoot] = getFilePath(tmpSettingType);
2459
2462
  if (settingType !== ``) {
@@ -2596,7 +2599,10 @@ const initialControl = async () => {
2596
2599
  }
2597
2600
  }
2598
2601
  g_customJsObj.preTitle.forEach(func => func());
2599
- titleInit();
2602
+ const queryMusicId = getQueryParamVal(`musicId`);
2603
+ g_settings.musicIdxNum = queryMusicId !== null ? Number(queryMusicId) :
2604
+ g_headerObj.musicNos[g_stateObj.scoreId] || g_headerObj.musicNos[0];
2605
+ titleInit(true);
2600
2606
 
2601
2607
  // 未使用のg_keyObjプロパティを削除
2602
2608
  const keyProp = g_keyCopyLists.simple.concat(g_keyCopyLists.multiple, `keyCtrl`, `keyName`, `minWidth`, `ptchara`);
@@ -2614,6 +2620,8 @@ const initialControl = async () => {
2614
2620
  }
2615
2621
  });
2616
2622
 
2623
+ g_stateObj.keyInitial = true;
2624
+
2617
2625
  // エディター用のフォーマッター作成
2618
2626
  const customKeyList = g_headerObj.keyLists.filter(val =>
2619
2627
  g_keyObj.defaultKeyList.findIndex(key => key === val) < 0);
@@ -2746,15 +2754,31 @@ const initialControl = async () => {
2746
2754
 
2747
2755
  /**
2748
2756
  * 作品別ローカルストレージの読み込み・初期設定
2757
+ * @param {string} _musicId 楽曲ID
2749
2758
  */
2750
- const loadLocalStorage = () => {
2751
- // URLからscoreId, h(高さ), debugを削除
2759
+ const loadLocalStorage = (_musicId = ``) => {
2760
+
2761
+ // 作品別ローカルストレージのキー(URL)取得のため、
2762
+ // scoreId, h, debug, musicIdを削除
2763
+ // 選択中の楽曲ID(_musicId)がある場合は、キーとして区別するため追加
2752
2764
  const url = new URL(location.href);
2753
2765
  url.searchParams.delete(`scoreId`);
2754
2766
  url.searchParams.delete(`h`);
2755
2767
  url.searchParams.delete(`debug`);
2768
+ url.searchParams.delete(`musicId`);
2756
2769
  g_localStorageUrl = url.toString();
2757
2770
 
2771
+ // リザルト表示用のURL組み立てのため、_musicIdのないURLを保存
2772
+ g_localStorageUrlOrg = g_localStorageUrl;
2773
+
2774
+ if (_musicId !== ``) {
2775
+ url.searchParams.append(`musicId`, _musicId);
2776
+ g_localStorageUrl = url.toString();
2777
+ if (g_langStorage.safeMode === C_FLG_ON) {
2778
+ return;
2779
+ }
2780
+ }
2781
+
2758
2782
  /**
2759
2783
  * ローカルストレージの初期値設定
2760
2784
  * @param {string} _name
@@ -3247,8 +3271,8 @@ const preheaderConvert = _dosObj => {
3247
3271
  });
3248
3272
 
3249
3273
  const convLocalPath = (_file, _type) =>
3250
- g_remoteFlg && hasVal(_file) && !_file.includes(`(..)`) && !hasRemoteDomain(_file)
3251
- ? `(..)../${_type}/${_file}`
3274
+ g_remoteFlg && hasVal(_file) && !_file.includes(C_MRK_CURRENT_DIRECTORY) && !hasRemoteDomain(_file)
3275
+ ? `${C_MRK_CURRENT_DIRECTORY}../${_type}/${_file}`
3252
3276
  : _file;
3253
3277
 
3254
3278
  // 外部スキンファイルの指定
@@ -3387,14 +3411,16 @@ const headerConvert = _dosObj => {
3387
3411
  obj.musicTitles = [];
3388
3412
  obj.musicTitlesForView = [];
3389
3413
  obj.artistNames = [];
3414
+ obj.artistUrls = [];
3390
3415
  obj.musicNos = [];
3416
+ obj.bpms = [];
3391
3417
 
3392
3418
  const dosMusicTitle = getHeader(_dosObj, `musicTitle`);
3393
3419
  if (hasVal(dosMusicTitle)) {
3394
3420
  const musicData = splitLF2(dosMusicTitle);
3395
3421
 
3396
3422
  if (hasVal(_dosObj.musicNo)) {
3397
- obj.musicNos = _dosObj.musicNo.split(`$`);
3423
+ obj.musicNos = splitLF2(_dosObj.musicNo).map(val => Number(val));
3398
3424
  }
3399
3425
 
3400
3426
  for (let j = 0; j < musicData.length; j++) {
@@ -3403,22 +3429,27 @@ const headerConvert = _dosObj => {
3403
3429
  if (obj.musicNos.length >= j) {
3404
3430
  obj.musicTitles[j] = escapeHtml(getMusicNameSimple(musics[0]));
3405
3431
  obj.musicTitlesForView[j] = escapeHtmlForArray(getMusicNameMultiLine(musics[0]));
3406
- obj.artistNames[j] = escapeHtml(musics[1] ?? ``);
3432
+ obj.artistNames[j] = escapeHtml(musics[1] || obj.artistNames[0] || ``);
3433
+ obj.artistUrls[j] = musics[2] || obj.artistUrls[0] || ``;
3434
+ obj.bpms[j] = musics[4] || obj.bpms[0] || `----`;
3435
+ }
3436
+
3437
+ // 選曲ではなく、単一作品用の項目としての管理変数を定義
3438
+ if (j === 0) {
3439
+ obj.musicTitle = obj.musicTitles[0];
3440
+ obj.musicTitleForView = obj.musicTitlesForView[0];
3441
+ obj.artistName = obj.artistNames[0];
3442
+ if (obj.artistName === ``) {
3443
+ makeWarningWindow(g_msgInfoObj.E_0011);
3444
+ obj.artistName = `artistName`;
3445
+ }
3446
+ obj.artistUrl = obj.artistUrls[0];
3447
+ if (hasVal(musics[3])) {
3448
+ obj.musicTitles[0] = escapeHtml(getMusicNameSimple(musics[3]));
3449
+ obj.musicTitlesForView[0] = escapeHtmlForArray(getMusicNameMultiLine(musics[3]));
3450
+ }
3407
3451
  }
3408
3452
  }
3409
- const musics = splitComma(musicData[0]);
3410
- obj.musicTitle = obj.musicTitles[0];
3411
- obj.musicTitleForView = obj.musicTitlesForView[0];
3412
- obj.artistName = obj.artistNames[0] ?? ``;
3413
- if (obj.artistName === ``) {
3414
- makeWarningWindow(g_msgInfoObj.E_0011);
3415
- obj.artistName = `artistName`;
3416
- }
3417
- obj.artistUrl = musics[2] ?? ``;
3418
- if (musics[3] !== undefined) {
3419
- obj.musicTitles[0] = escapeHtml(getMusicNameSimple(musics[3]));
3420
- obj.musicTitlesForView[0] = escapeHtmlForArray(getMusicNameMultiLine(musics[3]));
3421
- }
3422
3453
 
3423
3454
  } else {
3424
3455
  makeWarningWindow(g_msgInfoObj.E_0012);
@@ -3428,6 +3459,10 @@ const headerConvert = _dosObj => {
3428
3459
  obj.artistUrl = ``;
3429
3460
  }
3430
3461
 
3462
+ // 選曲機能の利用有無
3463
+ obj.packageNames = (_dosObj.packageName || ``).split(`<br>`);
3464
+ obj.musicSelectUse = _dosObj.packageName !== undefined;
3465
+
3431
3466
  // 最小・最大速度の設定
3432
3467
  obj.minSpeed = Math.round(setVal(_dosObj.minSpeed, C_MIN_SPEED, C_TYP_FLOAT) * 4) / 4;
3433
3468
  obj.maxSpeed = Math.round(setVal(_dosObj.maxSpeed, C_MAX_SPEED, C_TYP_FLOAT) * 4) / 4;
@@ -3450,10 +3485,17 @@ const headerConvert = _dosObj => {
3450
3485
 
3451
3486
  // 製作者表示
3452
3487
  const dosTuning = getHeader(_dosObj, `tuning`);
3488
+ obj.tuningNames = [];
3489
+ obj.tuningUrls = [];
3453
3490
  if (hasVal(dosTuning)) {
3454
- const tunings = dosTuning.split(`,`);
3455
- obj.tuning = escapeHtmlForEnabledTag(tunings[0]);
3456
- obj.creatorUrl = (tunings.length > 1 ? tunings[1] : (g_presetObj.tuningUrl ?? ``));
3491
+ splitLF2(dosTuning).forEach(tuning => {
3492
+ const tuningData = tuning.split(`,`);
3493
+ obj.tuningNames.push(escapeHtmlForEnabledTag(tuningData[0]));
3494
+ obj.tuningUrls.push(tuningData[1] ||
3495
+ (getHeader(g_presetObj, `tuning`) === tuningData[0] ? g_presetObj.tuningUrl : ``));
3496
+ });
3497
+ obj.tuning = obj.tuningNames[0];
3498
+ obj.creatorUrl = obj.tuningUrls[0] || g_presetObj.tuningUrl || ``;
3457
3499
  } else {
3458
3500
  obj.tuning = escapeHtmlForEnabledTag(getHeader(g_presetObj, `tuning`) ?? `name`);
3459
3501
  obj.creatorUrl = g_presetObj.tuningUrl ?? ``;
@@ -3537,6 +3579,9 @@ const headerConvert = _dosObj => {
3537
3579
  obj.viewLists = [...Array(obj.keyLabels.length).keys()];
3538
3580
  obj.keyLists = keyLists.sort((a, b) => parseInt(a) - parseInt(b));
3539
3581
  obj.undefinedKeyLists = obj.keyLists.filter(key => g_keyObj[`${g_keyObj.defaultProp}${key}_0`] === undefined);
3582
+ if (obj.musicNos.length === 0) {
3583
+ obj.musicNos = fillArray(obj.keyLabels.length);
3584
+ }
3540
3585
 
3541
3586
  // 譜面変更セレクターの利用有無
3542
3587
  obj.difSelectorUse = getDifSelectorUse(_dosObj.difSelectorUse, obj.viewLists);
@@ -3636,18 +3681,18 @@ const headerConvert = _dosObj => {
3636
3681
  obj.blankFrameDefs = [200];
3637
3682
  if (isNaN(parseFloat(_dosObj.blankFrame))) {
3638
3683
  } else {
3639
- obj.blankFrameDefs = _dosObj.blankFrame.split(`$`).map(val => parseInt(val));
3684
+ obj.blankFrameDefs = splitLF2(_dosObj.blankFrame).map(val => parseInt(val));
3640
3685
  }
3641
3686
  obj.blankFrame = obj.blankFrameDefs[0];
3642
3687
  obj.blankFrameDef = obj.blankFrameDefs[0];
3643
3688
 
3644
3689
  // 開始フレーム数(0以外の場合はフェードインスタート)、終了フレーム数
3645
3690
  [`startFrame`, `endFrame`].filter(tmpParam => hasVal(_dosObj[tmpParam]))
3646
- .forEach(param => obj[param] = _dosObj[param].split(`$`).map(frame => transTimerToFrame(frame)));
3691
+ .forEach(param => obj[param] = splitLF2(_dosObj[param]).map(frame => transTimerToFrame(frame)));
3647
3692
 
3648
3693
  // フェードアウトフレーム数(譜面別)
3649
3694
  if (hasVal(_dosObj.fadeFrame)) {
3650
- const fadeFrames = _dosObj.fadeFrame.split(`$`);
3695
+ const fadeFrames = splitLF2(_dosObj.fadeFrame);
3651
3696
  obj.fadeFrame = [];
3652
3697
  fadeFrames.forEach((fadeInfo, j) => {
3653
3698
  obj.fadeFrame[j] = fadeInfo.split(`,`);
@@ -3692,7 +3737,7 @@ const headerConvert = _dosObj => {
3692
3737
  g_diffObj.frzJdgY = (isNaN(parseFloat(_dosObj.frzJdgY)) ? 0 : parseFloat(_dosObj.frzJdgY));
3693
3738
 
3694
3739
  // musicフォルダ設定
3695
- obj.musicFolder = _dosObj.musicFolder ?? (g_remoteFlg ? `(..)../music` : `music`);
3740
+ obj.musicFolder = _dosObj.musicFolder ?? (g_remoteFlg ? `${C_MRK_CURRENT_DIRECTORY}../music` : `music`);
3696
3741
 
3697
3742
  // 楽曲URL
3698
3743
  if (hasVal(_dosObj.musicUrl)) {
@@ -3802,8 +3847,9 @@ const headerConvert = _dosObj => {
3802
3847
  g_stateObj.excessive = boolToSwitch(obj.excessiveJdgUse);
3803
3848
  g_settings.excessiveNum = Number(obj.excessiveJdgUse);
3804
3849
 
3805
- // 譜面名に制作者名を付加するかどうかのフラグ
3850
+ // 譜面名に制作者名を付加するかどうかのフラグ(選曲用に初期値を退避)
3806
3851
  obj.makerView = setBoolVal(_dosObj.makerView);
3852
+ obj.makerViewOrg = obj.makerView;
3807
3853
 
3808
3854
  // shuffleUse=group 時のみshuffle用配列を組み替える
3809
3855
  if (_dosObj.shuffleUse === `group`) {
@@ -3903,11 +3949,10 @@ const headerConvert = _dosObj => {
3903
3949
  const tmpComment = (_dosObj[`commentVal${g_localeObj.val}`] ?? _dosObj.commentVal ?? ``).split(`\r\n`).join(`\n`);
3904
3950
  obj.commentVal = tmpComment.split(`\n`).join(newlineTag);
3905
3951
 
3906
- // クレジット表示
3907
- if (document.getElementById(`webMusicTitle`) !== null) {
3908
- webMusicTitle.innerHTML =
3909
- `<span style="font-size:${wUnit(32)}">${obj.musicTitleForView.join(`<br>`)}</span><br>
3910
- <span style="font-size:${wUnit(16)}">(Artist: <a href="${obj.artistUrl}" target="_blank">${obj.artistName}</a>)</span>`;
3952
+ const maxMusicNo = Math.max(...obj.musicNos);
3953
+ for (let j = 0; j <= maxMusicNo; j++) {
3954
+ obj[`commentVal${j}`] = (_dosObj[`commentVal${j}`] || ``).split(`\n`)
3955
+ .filter((val, k) => k !== 0 || val !== ``).join(`<br>`);
3911
3956
  }
3912
3957
 
3913
3958
  // コメントの外部化設定
@@ -4776,9 +4821,10 @@ const setKeyDfVal = _ptnName => {
4776
4821
  /*-----------------------------------------------------------*/
4777
4822
 
4778
4823
  /**
4779
- * タイトル画面初期化
4824
+ * タイトル画面初期化
4825
+ * @param {boolean} _initFlg 初期化フラグ
4780
4826
  */
4781
- const titleInit = () => {
4827
+ const titleInit = (_initFlg = false) => {
4782
4828
 
4783
4829
  clearWindow(true);
4784
4830
  g_currentPage = `title`;
@@ -4810,20 +4856,6 @@ const titleInit = () => {
4810
4856
  let buffTime;
4811
4857
  let titleStartTime = performance.now();
4812
4858
 
4813
- // 背景の矢印オブジェクトを表示
4814
- if (!g_headerObj.customTitleArrowUse) {
4815
- divRoot.appendChild(
4816
- createColorObject2(`lblArrow`, {
4817
- x: (g_sWidth - 500) / 2, y: -15 + (g_sHeight - 500) / 2,
4818
- w: 500, h: 500, rotateEnabled: true,
4819
- background: makeColorGradation(g_headerObj.titlearrowgrds[0] || g_headerObj.setColorOrg[0], {
4820
- _defaultColorgrd: [false, `#eeeeee`],
4821
- _objType: `titleArrow`,
4822
- }), rotate: `titleArrow:${g_headerObj.titleArrowRotate}`,
4823
- })
4824
- );
4825
- }
4826
-
4827
4859
  // 背景スプライトを作成
4828
4860
  createMultipleSprite(`backTitleSprite`, g_headerObj.backTitleMaxDepth);
4829
4861
 
@@ -4836,76 +4868,123 @@ const titleInit = () => {
4836
4868
  .replace(/[\t\n]/g, ``), 0, 15, g_cssObj.flex_centering)
4837
4869
  );
4838
4870
 
4839
- // 曲名文字描画(曲名は譜面データから取得)
4840
- if (!g_headerObj.customTitleUse) {
4871
+ // 背景の矢印オブジェクトを表示
4872
+ const tmpCreatorList = [];
4873
+ if (g_headerObj.musicSelectUse) {
4874
+ if (getQueryParamVal(`scoreId`) !== null) {
4875
+ g_headerObj.viewLists = [];
4876
+ g_headerObj.musicNos.forEach((val, j) => {
4877
+ if (val === g_settings.musicIdxNum) {
4878
+ g_headerObj.viewLists.push(j);
4879
+ tmpCreatorList.push(g_headerObj.creatorNames[j]);
4880
+ }
4881
+ });
4882
+ divRoot.appendChild(drawBackArrow(g_headerObj.viewLists[0] + 1));
4883
+ loadLocalStorage(g_settings.musicIdxNum);
4884
+ makeInfoWindow(g_msgInfoObj.W_0041.split(`{0}`).join(g_localStorageUrl), ``,
4885
+ { _backColor: `#333333`, _textColor: `#cccccc`, _pointerEvents: C_DIS_INHERIT });
4886
+ }
4887
+ } else if (!g_headerObj.customTitleArrowUse) {
4888
+ divRoot.appendChild(drawBackArrow());
4889
+ }
4841
4890
 
4842
- // グラデーションの指定がない場合、
4843
- // 矢印色の1番目と3番目を使ってタイトルをグラデーション
4844
- const titlegrd1 = g_headerObj.titlegrds[0] || `${g_headerObj.setColorOrg[0]}:${g_headerObj.setColorOrg[2]}`;
4845
- const titlegrd2 = g_headerObj.titlegrds[1] || titlegrd1;
4891
+ let wheelHandler;
4892
+ if (g_headerObj.musicSelectUse && getQueryParamVal(`scoreId`) === null) {
4846
4893
 
4847
- const titlegrds = [];
4848
- [titlegrd1, titlegrd2].forEach((titlegrd, j) =>
4849
- titlegrds[j] = makeColorGradation(titlegrd, { _defaultColorgrd: false, _objType: `titleMusic` }));
4894
+ // 選曲画面の初期化
4895
+ const wheelCycle = 2;
4896
+ const musicMaxIdx = Math.max(...g_headerObj.musicNos);
4897
+ const musicIdxTmpList = [...Array(musicMaxIdx + 1).keys()];
4898
+
4899
+ /**
4900
+ * メイン以外の選曲ボタンの作成
4901
+ * @param {number} _heightPos
4902
+ * @returns {HTMLDivElement}
4903
+ */
4904
+ const createMSelectBtn = (_heightPos) => createCss2Button(`btnMusicSelect${_heightPos}`,
4905
+ ``, () => changeMSelect(_heightPos), {
4906
+ x: g_btnX(1 / 3) + Math.abs(_heightPos) * 10,
4907
+ y: g_sHeight / 2 + _heightPos * 30 + (_heightPos > 0 ? 1 : -1) * 90,
4908
+ w: g_btnWidth(1 / 2), h: 27, siz: 14, border: `solid 1px #666666`,
4909
+ align: C_ALIGN_LEFT, padding: `0 10px`,
4910
+ }, g_cssObj.button_Default_NoColor, g_cssObj.title_base);
4850
4911
 
4851
- let titlefontsize = 64;
4852
- for (let j = 0; j < g_headerObj.musicTitleForView.length; j++) {
4853
- if (g_headerObj.musicTitleForView[j] !== ``) {
4854
- titlefontsize = getFontSize(g_headerObj.musicTitleForView[j], g_sWidth - 100, g_headerObj.titlefonts[j], titlefontsize);
4912
+ for (let j = -g_settings.mSelectableTerms; j <= g_settings.mSelectableTerms; j++) {
4913
+ if (j !== 0) {
4914
+ divRoot.appendChild(createMSelectBtn(j));
4855
4915
  }
4856
4916
  }
4917
+ createEmptySprite(divRoot, `keyTitleSprite`, g_windowObj.keyTitleSprite);
4918
+ multiAppend(divRoot,
4919
+ createDivCss2Label(`lblMusicSelect`, ``, g_lblPosObj.lblMusicSelect),
4920
+ createDivCss2Label(`lblMusicSelectDetail`, ``, g_lblPosObj.lblMusicSelectDetail),
4921
+ createCss2Button(`btnStart`,
4922
+ `>`, () => {
4923
+ clearTimeout(g_timeoutEvtTitleId);
4924
+ g_handler.removeListener(wheelHandler);
4925
+ g_keyObj.prevKey = `Dummy${g_settings.musicIdxNum}`;
4926
+ }, Object.assign({
4927
+ resetFunc: () => optionInit(),
4928
+ }, g_lblPosObj.btnStart_music), g_cssObj.button_Tweet),
4929
+ createCss2Button(`btnMusicSelectPrev`, `↑`, () => changeMSelect(-1),
4930
+ g_lblPosObj.btnMusicSelectPrev, g_cssObj.button_Setting),
4931
+ createCss2Button(`btnMusicSelectNext`, `↓`, () => changeMSelect(1),
4932
+ g_lblPosObj.btnMusicSelectNext, g_cssObj.button_Setting),
4933
+ createCss2Button(`btnMusicSelectRandom`, `Random`, () =>
4934
+ changeMSelect(Math.floor(Math.random() * musicIdxTmpList.length)),
4935
+ g_lblPosObj.btnMusicSelectRandom, g_cssObj.button_Default),
4936
+ createDivCss2Label(`lblMusicCnt`, ``, g_lblPosObj.lblMusicCnt),
4937
+ createDivCss2Label(`lblComment`, ``, g_lblPosObj.lblComment_music),
4938
+ );
4939
+ changeMSelect(0, _initFlg);
4857
4940
 
4858
- // 変数 titlesize の定義 (使用例: |titlesize=40$20|)
4859
- const titlefontsizes = (g_headerObj.titlesize?.split(`$`).join(`,`).split(`,`) || [titlefontsize, titlefontsize]);
4860
- const titlefontsize1 = setIntVal(titlefontsizes[0], titlefontsize);
4861
- const titlefontsize2 = setIntVal(titlefontsizes[1], titlefontsize1);
4941
+ let wheelCnt = 0;
4942
+ wheelHandler = g_handler.addListener(divRoot, `wheel`, e => {
4943
+ if (g_stateObj.keyInitial && wheelCnt === 0) {
4944
+ e.preventDefault();
4945
+ changeMSelect(e.deltaY > 0 ? 1 : -1);
4946
+ }
4947
+ wheelCnt = (wheelCnt + 1) % wheelCycle;
4948
+ });
4862
4949
 
4863
- // 変数 titlelineheight の定義 (使用例: |titlelineheight=50|)
4864
- const titlelineheight = (g_headerObj.titlelineheight !== `` ? g_headerObj.titlelineheight - (titlefontsize2 + 10) : 0);
4950
+ // 初期表示用 (2秒後に選曲画面を表示)
4951
+ if (_initFlg && !g_headerObj.customTitleUse) {
4952
+ const mSelectTitleSprite = createEmptySprite(divRoot, `mSelectTitleSprite`,
4953
+ g_windowObj.mSelectTitleSprite, g_cssObj.settings_DifSelector);
4954
+ multiAppend(mSelectTitleSprite,
4955
+ drawBackArrow(),
4956
+ drawTitle(g_headerObj.packageNames),
4957
+ );
4865
4958
 
4866
- const txtAnimations = [``, ``];
4867
- if (!g_headerObj.customTitleAnimationUse) {
4868
- for (let j = 0; j < txtAnimations.length; j++) {
4869
- txtAnimations[j] = `animation-name:${g_headerObj.titleAnimationName[j]};
4870
- animation-duration:${g_headerObj.titleAnimationDuration[j]}s;
4871
- animation-delay:${g_headerObj.titleAnimationDelay[j]}s;
4872
- animation-timing-function:${g_headerObj.titleAnimationTimingFunction[j]};`;
4873
- }
4959
+ let spriteOpacity = 1;
4960
+ let fadeOpacity = null;
4961
+ const fadeStartOpacity = setTimeout(() => {
4962
+ clearTimeout(fadeStartOpacity);
4963
+ setOpacity(spriteOpacity);
4964
+ }, 2000);
4965
+
4966
+ const setOpacity = (_opacity) => {
4967
+ if (_opacity <= 0) {
4968
+ clearTimeout(fadeOpacity);
4969
+ mSelectTitleSprite.style.display = C_DIS_NONE;
4970
+ } else {
4971
+ mSelectTitleSprite.style.opacity = _opacity;
4972
+ fadeOpacity = setTimeout(() => {
4973
+ spriteOpacity -= 0.25;
4974
+ setOpacity(spriteOpacity);
4975
+ }, 50);
4976
+ }
4977
+ };
4874
4978
  }
4875
- const lblmusicTitle = createDivCss2Label(`lblmusicTitle`,
4876
- `<div id="lblmusicTitle1" style="
4877
- font-family:${g_headerObj.titlefonts[0]};
4878
- background: ${titlegrds[0]};
4879
- background-clip: text;
4880
- -webkit-background-clip: text;
4881
- color: rgba(255,255,255,0.0);
4882
- ${txtAnimations[0]}
4883
- " class="${g_headerObj.titleAnimationClass[0]}">
4884
- ${g_headerObj.musicTitleForView[0]}
4885
- </div>
4886
- <div id="lblmusicTitle2" style="
4887
- font-size:${wUnit(titlefontsize2)};
4888
- position:relative;left:${wUnit(g_headerObj.titlepos[1][0])};
4889
- top:${wUnit(g_headerObj.titlepos[1][1] + titlelineheight)};
4890
- font-family:${g_headerObj.titlefonts[1]};
4891
- background: ${titlegrds[1]};
4892
- background-clip: text;
4893
- -webkit-background-clip: text;
4894
- color: rgba(255,255,255,0.0);
4895
- ${txtAnimations[1]}
4896
- " class="${g_headerObj.titleAnimationClass[1]}">
4897
- ${g_headerObj.musicTitleForView[1] ?? ``}
4898
- </div>
4899
- `,
4900
- {
4901
- x: Number(g_headerObj.titlepos[0][0]), y: Number(g_headerObj.titlepos[0][1]),
4902
- w: g_sWidth, h: g_sHeight - 40, siz: titlefontsize1,
4903
- display: `flex`, flexDirection: `column`, justifyContent: `center`, alignItems: `center`,
4904
- }
4905
- );
4906
- divRoot.appendChild(lblmusicTitle);
4979
+ } else if (!g_headerObj.customTitleUse) {
4980
+ // 曲名文字描画(曲名は譜面データから取得)
4981
+ divRoot.appendChild(drawTitle(g_headerObj.musicTitlesForView[g_settings.musicIdxNum],
4982
+ g_headerObj.musicSelectUse ? g_headerObj.viewLists[0] + 1 : ``))
4907
4983
  }
4908
4984
 
4985
+ // クレジット表示
4986
+ externalWebTitle();
4987
+
4909
4988
  if (g_errMsgObj.title !== ``) {
4910
4989
  makeWarningWindow();
4911
4990
  }
@@ -4936,17 +5015,38 @@ const titleInit = () => {
4936
5015
  createCss2Button(_id, _text, () => true,
4937
5016
  Object.assign(g_lblPosObj[_id], { siz: getLinkSiz(_text), whiteSpace: `normal`, resetFunc: () => openLink(_url) }), g_cssObj.button_Default);
4938
5017
 
4939
- // ボタン描画
4940
- multiAppend(divRoot,
5018
+ if (g_headerObj.musicSelectUse && getQueryParamVal(`scoreId`) === null) {
5019
+ // 選曲モードではクレジット表示は別で行われているため表示しない
5020
+ } else {
5021
+ if (tmpCreatorList.length === 0) {
5022
+ tmpCreatorList.push(g_headerObj.creatorNames[0]);
5023
+ }
5024
+ const [creatorName, creatorUrl] = getCreatorInfo(tmpCreatorList);
4941
5025
 
4942
- // Click Here
4943
- createCss2Button(`btnStart`, g_lblNameObj.clickHere, () => clearTimeout(g_timeoutEvtTitleId), {
4944
- x: g_btnX(), w: g_btnWidth(), siz: g_limitObj.titleSiz, resetFunc: () => optionInit(),
4945
- }, g_cssObj.button_Start),
5026
+ multiAppend(divRoot,
5027
+
5028
+ // Click Here
5029
+ createCss2Button(`btnStart`, g_lblNameObj.clickHere, () => {
5030
+ clearTimeout(g_timeoutEvtTitleId);
5031
+ g_keyObj.prevKey = `Dummy${g_settings.musicIdxNum}`;
5032
+ }, {
5033
+ x: g_btnX(), w: g_btnWidth(), siz: g_limitObj.titleSiz, resetFunc: () => optionInit(),
5034
+ }, g_cssObj.button_Start),
5035
+
5036
+ // 製作者表示
5037
+ createCreditBtn(`lnkMaker`, `${g_lblNameObj.maker}: ${g_headerObj.musicSelectUse ? creatorName : g_headerObj.tuningInit}`, creatorUrl),
5038
+
5039
+ // アーティスト表示
5040
+ createCreditBtn(`lnkArtist`, `${g_lblNameObj.artist}: ${g_headerObj.artistNames[g_settings.musicIdxNum]}`, g_headerObj.artistUrls[g_settings.musicIdxNum]),
5041
+ );
5042
+ }
5043
+
5044
+ multiAppend(divRoot,
4946
5045
 
4947
5046
  // Reset
4948
5047
  createCss2Button(`btnReset`, g_lblNameObj.dataReset, () => {
4949
5048
  clearTimeout(g_timeoutEvtTitleId);
5049
+ g_handler.removeListener(wheelHandler);
4950
5050
  dataMgtInit();
4951
5051
  }, g_lblPosObj.btnReset, g_cssObj.button_Reset),
4952
5052
 
@@ -4967,12 +5067,6 @@ const titleInit = () => {
4967
5067
  resetFunc: () => openLink(g_lblNameObj.helpUrl),
4968
5068
  }), g_cssObj.button_Setting),
4969
5069
 
4970
- // 製作者表示
4971
- createCreditBtn(`lnkMaker`, `${g_lblNameObj.maker}: ${g_headerObj.tuningInit}`, g_headerObj.creatorUrl),
4972
-
4973
- // アーティスト表示
4974
- createCreditBtn(`lnkArtist`, `${g_lblNameObj.artist}: ${g_headerObj.artistName}`, g_headerObj.artistUrl),
4975
-
4976
5070
  // バージョン描画
4977
5071
  createCss2Button(`lnkVersion`, versionName, () => true,
4978
5072
  Object.assign(g_lblPosObj.lnkVersion, {
@@ -5042,6 +5136,200 @@ const titleInit = () => {
5042
5136
  g_skinJsObj.title.forEach(func => func());
5043
5137
  };
5044
5138
 
5139
+ /**
5140
+ * 外部のタイトル表示
5141
+ */
5142
+ const externalWebTitle = () => {
5143
+ if (document.getElementById(`webMusicTitle`) !== null) {
5144
+ webMusicTitle.innerHTML =
5145
+ `<span style="font-size:${wUnit(32)}">${g_headerObj.musicTitlesForView[g_settings.musicIdxNum].join(`<br>`)}</span><br>
5146
+ <span style="font-size:${wUnit(16)}">(Artist: <a href="${g_headerObj.artistUrls[g_settings.musicIdxNum]}" target="_blank">${g_headerObj.artistNames[g_settings.musicIdxNum]}</a>)</span>`;
5147
+ }
5148
+ };
5149
+
5150
+ /**
5151
+ * 背景矢印の表示
5152
+ * @param {string|number} _scoreId
5153
+ * @returns {HTMLDivElement}
5154
+ */
5155
+ const drawBackArrow = (_scoreId = ``) =>
5156
+ createColorObject2(`lblArrow`, {
5157
+ x: (g_sWidth - 500) / 2, y: -15 + (g_sHeight - 500) / 2,
5158
+ w: 500, h: 500, rotateEnabled: true,
5159
+ background: makeColorGradation(g_headerObj.titlearrowgrds[0] ||
5160
+ g_headerObj[`setColor${_scoreId}Org`]?.[0] || g_headerObj.setColorOrg[0], {
5161
+ _defaultColorgrd: [false, `#eeeeee`],
5162
+ _objType: `titleArrow`,
5163
+ }), rotate: `titleArrow:${g_headerObj.titleArrowRotate}`,
5164
+ });
5165
+
5166
+ /**
5167
+ * タイトル文字の表示
5168
+ * @param {string[]} _titleName
5169
+ * @returns {HTMLDivElement}
5170
+ */
5171
+ const drawTitle = (_titleName = g_headerObj.musicTitleForView, _scoreId = ``) => {
5172
+
5173
+ // グラデーションの指定がない場合、
5174
+ // 矢印色の1番目と3番目を使ってタイトルをグラデーション
5175
+ const titlegrd1 = g_headerObj.titlegrds[0] || (g_headerObj[`setColor${_scoreId}Org`] ?
5176
+ `${g_headerObj[`setColor${_scoreId}Org`][0]}:${g_headerObj[`setColor${_scoreId}Org`][2]}` : `${g_headerObj.setColorOrg[0]}:${g_headerObj.setColorOrg[2]}`);
5177
+ const titlegrd2 = g_headerObj.titlegrds[1] || titlegrd1;
5178
+
5179
+ const titlegrds = [];
5180
+ [titlegrd1, titlegrd2].forEach((titlegrd, j) =>
5181
+ titlegrds[j] = makeColorGradation(titlegrd, { _defaultColorgrd: false, _objType: `titleMusic` }));
5182
+
5183
+ let titlefontsize = 64;
5184
+ for (let j = 0; j < _titleName.length; j++) {
5185
+ if (_titleName[j] !== ``) {
5186
+ titlefontsize = getFontSize(_titleName[j], g_sWidth - 100, g_headerObj.titlefonts[j], titlefontsize);
5187
+ }
5188
+ }
5189
+
5190
+ // 変数 titlesize の定義 (使用例: |titlesize=40$20|)
5191
+ const titlefontsizes = (g_headerObj.titlesize?.split(`$`).join(`,`).split(`,`) || [titlefontsize, titlefontsize]);
5192
+ const titlefontsize1 = setIntVal(titlefontsizes[0], titlefontsize);
5193
+ const titlefontsize2 = setIntVal(titlefontsizes[1], titlefontsize1);
5194
+
5195
+ // 変数 titlelineheight の定義 (使用例: |titlelineheight=50|)
5196
+ const titlelineheight = (g_headerObj.titlelineheight !== `` ? g_headerObj.titlelineheight - (titlefontsize2 + 10) : 0);
5197
+
5198
+ const txtAnimations = [``, ``];
5199
+ if (!g_headerObj.customTitleAnimationUse) {
5200
+ for (let j = 0; j < txtAnimations.length; j++) {
5201
+ txtAnimations[j] = `animation-name:${g_headerObj.titleAnimationName[j]};
5202
+ animation-duration:${g_headerObj.titleAnimationDuration[j]}s;
5203
+ animation-delay:${g_headerObj.titleAnimationDelay[j]}s;
5204
+ animation-timing-function:${g_headerObj.titleAnimationTimingFunction[j]};`;
5205
+ }
5206
+ }
5207
+ return createDivCss2Label(`lblmusicTitle`,
5208
+ `<div id="lblmusicTitle1" style="
5209
+ font-family:${g_headerObj.titlefonts[0]};
5210
+ background: ${titlegrds[0]};
5211
+ background-clip: text;
5212
+ -webkit-background-clip: text;
5213
+ color: rgba(255,255,255,0.0);
5214
+ ${txtAnimations[0]}
5215
+ " class="${g_headerObj.titleAnimationClass[0]}">
5216
+ ${_titleName[0]}
5217
+ </div>
5218
+ <div id="lblmusicTitle2" style="
5219
+ font-size:${wUnit(titlefontsize2)};
5220
+ position:relative;left:${wUnit(g_headerObj.titlepos[1][0])};
5221
+ top:${wUnit(g_headerObj.titlepos[1][1] + titlelineheight)};
5222
+ font-family:${g_headerObj.titlefonts[1]};
5223
+ background: ${titlegrds[1]};
5224
+ background-clip: text;
5225
+ -webkit-background-clip: text;
5226
+ color: rgba(255,255,255,0.0);
5227
+ ${txtAnimations[1]}
5228
+ " class="${g_headerObj.titleAnimationClass[1]}">
5229
+ ${_titleName[1] ?? ``}
5230
+ </div>
5231
+ `,
5232
+ {
5233
+ x: Number(g_headerObj.titlepos[0][0]), y: Number(g_headerObj.titlepos[0][1]),
5234
+ w: g_sWidth, h: g_sHeight - 40, siz: titlefontsize1,
5235
+ display: `flex`, flexDirection: `column`, justifyContent: `center`, alignItems: `center`,
5236
+ }
5237
+ );
5238
+ };
5239
+
5240
+ /**
5241
+ * 製作者情報の取得
5242
+ * @param {string[]} _creatorList
5243
+ * @returns [string, string, number]
5244
+ */
5245
+ const getCreatorInfo = (_creatorList) => {
5246
+ const creatorName = makeDedupliArray(_creatorList).length === 1 ? _creatorList[0] : `Various`;
5247
+ g_headerObj.makerView = g_headerObj.makerViewOrg ? true : creatorName === `Various`;
5248
+ const creatorIdx = g_headerObj.tuningNames.findIndex(val => val === creatorName);
5249
+ const creatorUrl = creatorIdx >= 0 ? g_headerObj.tuningUrls[creatorIdx] : ``;
5250
+ return [creatorName, creatorUrl, creatorIdx];
5251
+ }
5252
+
5253
+ /**
5254
+ * 選曲ボタンを押したときの処理
5255
+ * @param {number} _num
5256
+ * @param {boolean} _initFlg
5257
+ */
5258
+ const changeMSelect = (_num, _initFlg = false) => {
5259
+ const limitedMLength = 35;
5260
+ const musicMaxIdx = Math.max(...g_headerObj.musicNos);
5261
+ const musicIdxTmpList = [...Array(musicMaxIdx + 1).keys()];
5262
+
5263
+ // 選択方向に合わせて楽曲リスト情報を再取得
5264
+ for (let j = -g_settings.mSelectableTerms; j <= g_settings.mSelectableTerms; j++) {
5265
+ const idx = (j + _num + g_settings.musicIdxNum + musicIdxTmpList.length * 10) % musicIdxTmpList.length;
5266
+ if (j === 0) {
5267
+ } else {
5268
+ document.getElementById(`btnMusicSelect${j}`).style.fontSize =
5269
+ getFontSize(g_headerObj.musicTitles[idx].slice(0, limitedMLength), g_btnWidth(1 / 2), getBasicFont(), 14);
5270
+ document.getElementById(`btnMusicSelect${j}`).innerHTML =
5271
+ `${g_headerObj.musicTitles[idx].slice(0, limitedMLength)}${g_headerObj.musicTitles[idx].length > limitedMLength ? '...' : ''}<br>` +
5272
+ `<span style="font-size:0.7em;line-height:9px"> / ${g_headerObj.artistNames[idx]}</span>`;
5273
+ }
5274
+ }
5275
+ // 現在選択中の楽曲IDを再設定
5276
+ g_settings.musicIdxNum = (g_settings.musicIdxNum + _num + musicIdxTmpList.length) % musicIdxTmpList.length;
5277
+
5278
+ // 選択した楽曲に対応する譜面番号、製作者情報、曲長を取得
5279
+ g_headerObj.viewLists = [];
5280
+ const tmpKeyList = [], tmpCreatorList = [], tmpPlayingFrameList = [];
5281
+ g_headerObj.musicNos.forEach((val, j) => {
5282
+ if (val === (g_settings.musicIdxNum + musicIdxTmpList.length * 20) %
5283
+ musicIdxTmpList.length) {
5284
+ g_headerObj.viewLists.push(j);
5285
+ tmpKeyList.push(g_headerObj.keyLabels[j]);
5286
+ tmpCreatorList.push(g_headerObj.creatorNames[j]);
5287
+ tmpPlayingFrameList.push(g_detailObj.playingFrameWithBlank[j]);
5288
+ }
5289
+ });
5290
+ const playingFrames = makeDedupliArray(tmpPlayingFrameList.sort((a, b) => a - b).map(val => transFrameToTimer(val))).join(`, `);
5291
+ const [creatorName, creatorUrl, creatorIdx] = getCreatorInfo(tmpCreatorList);
5292
+ const creatorLink = creatorIdx >= 0 ?
5293
+ `<a href="${creatorUrl}" target="_blank">${creatorName}</a>` : creatorName;
5294
+
5295
+ // 選択した楽曲の情報表示
5296
+ const idx = g_settings.musicIdxNum;
5297
+ document.getElementById(`lblMusicSelect`).innerHTML =
5298
+ `<span style="font-size:${getFontSize(g_headerObj.musicTitlesForView[idx].join(`<br>`), g_btnWidth(1 / 2), getBasicFont(), 18)}px;` +
5299
+ `font-weight:bold">${g_headerObj.musicTitlesForView[idx].join(`<br>`)}</span>`;
5300
+ document.getElementById(`lblMusicSelectDetail`).innerHTML =
5301
+ `Maker: ${creatorLink} / Artist: <a href="${g_headerObj.artistUrls[idx]}" target="_blank">` +
5302
+ `${g_headerObj.artistNames[idx]}</a><br>Duration: ${playingFrames} / BPM: ${g_headerObj.bpms[idx]}`;
5303
+
5304
+ // 選択した楽曲で使われているキー種の一覧を作成
5305
+ deleteChildspriteAll(`keyTitleSprite`);
5306
+ makeDedupliArray(tmpKeyList).sort((a, b) => parseInt(a) - parseInt(b))
5307
+ .forEach((val, j) => keyTitleSprite.appendChild(
5308
+ createDivCss2Label(`btnKeyTitle${val}`, val,
5309
+ Object.assign({ x: 10 + j * 40 }, g_lblPosObj.btnKeyTitle)
5310
+ )));
5311
+
5312
+
5313
+ // 選択した楽曲の選択位置を表示
5314
+ lblMusicCnt.innerHTML = `${g_settings.musicIdxNum + 1} / ${musicMaxIdx + 1}`;
5315
+
5316
+ // 楽曲別のローカルストレージを再取得
5317
+ loadLocalStorage(g_settings.musicIdxNum);
5318
+ viewKeyStorage.cache = new Map();
5319
+
5320
+ // 初期化もしくは楽曲変更時に速度を初期化
5321
+ if (_initFlg || _num !== 0) {
5322
+ g_stateObj.speed = g_headerObj.initSpeeds[g_headerObj.viewLists[0]];
5323
+ g_settings.speedNum = getCurrentNo(g_settings.speeds, g_stateObj.speed);
5324
+ }
5325
+
5326
+ // コメント文の加工
5327
+ lblComment.innerHTML = convertStrToVal(g_headerObj[`commentVal${g_settings.musicIdxNum}`]);
5328
+
5329
+ // 選曲変更時のカスタム関数実行
5330
+ g_customJsObj.musicSelect.forEach(func => func(g_settings.musicIdxNum));
5331
+ };
5332
+
5045
5333
  /**
5046
5334
  * 警告用ウィンドウ(汎用)を表示
5047
5335
  * @param {string} _text
@@ -5074,10 +5362,12 @@ const makeWarningWindow = (_text = ``, { resetFlg = false, backBtnUse = false }
5074
5362
  * @param {string} _text
5075
5363
  * @param {string} _animationName
5076
5364
  * @param {string} [object._backColor='#ccccff']
5365
+ * @param {string} [object._textColor='#000066']
5366
+ * @param {string} [object._pointerEvents=C_DIS_NONE]
5077
5367
  */
5078
- const makeInfoWindow = (_text, _animationName = ``, { _backColor = `#ccccff` } = {}) => {
5079
- const lblWarning = setWindowStyle(`<p>${_text}</p>`, _backColor, `#000066`, C_ALIGN_CENTER);
5080
- lblWarning.style.pointerEvents = C_DIS_NONE;
5368
+ const makeInfoWindow = (_text, _animationName = ``, { _backColor = `#ccccff`, _textColor = `#000066`, _pointerEvents = C_DIS_NONE } = {}) => {
5369
+ const lblWarning = setWindowStyle(`<p>${_text}</p>`, _backColor, _textColor, C_ALIGN_CENTER);
5370
+ lblWarning.style.pointerEvents = _pointerEvents;
5081
5371
 
5082
5372
  if (_animationName !== ``) {
5083
5373
  lblWarning.style.animationName = _animationName;
@@ -5319,7 +5609,7 @@ const dataMgtInit = () => {
5319
5609
  }
5320
5610
  });
5321
5611
  reloadFlg = true;
5322
- sessionStorage.setItem('resetBackup', JSON.stringify(Array.from(backupData.entries())));
5612
+ sessionStorage.setItem(`resetBackup${g_settings.musicIdxNum}`, JSON.stringify(Array.from(backupData.entries())));
5323
5613
  }
5324
5614
  }, Object.assign(g_lblPosObj.btnResetN, {
5325
5615
  visibility: g_langStorage.safeMode === C_FLG_OFF ? C_DIS_INHERIT : `hidden`,
@@ -5332,7 +5622,7 @@ const dataMgtInit = () => {
5332
5622
 
5333
5623
  // リカバリー用のボタン
5334
5624
  createCss2Button(`btnUndo`, g_lblNameObj.b_undo, () => {
5335
- const backup = JSON.parse(sessionStorage.getItem('resetBackup'));
5625
+ const backup = JSON.parse(sessionStorage.getItem(`resetBackup${g_settings.musicIdxNum}`));
5336
5626
  if (backup && window.confirm(g_msgObj.dataRestoreConfirm)) {
5337
5627
  backup.forEach(([key, data]) => {
5338
5628
  if (g_resetFunc.has(key) || keyList.includes(key.slice(`XX`.length))) {
@@ -5342,12 +5632,12 @@ const dataMgtInit = () => {
5342
5632
  localStorage.setItem(`danonicw-${key}k`, JSON.stringify(data));
5343
5633
  }
5344
5634
  });
5345
- sessionStorage.removeItem('resetBackup');
5635
+ sessionStorage.removeItem(`resetBackup${g_settings.musicIdxNum}`);
5346
5636
  location.reload();
5347
5637
  }
5348
5638
  }, g_lblPosObj.btnUndo, g_cssObj.button_Tweet)
5349
5639
  );
5350
- if (sessionStorage.getItem('resetBackup') === null) {
5640
+ if (sessionStorage.getItem(`resetBackup${g_settings.musicIdxNum}`) === null) {
5351
5641
  btnUndo.style.display = C_DIS_NONE;
5352
5642
  }
5353
5643
 
@@ -5538,6 +5828,15 @@ const optionInit = () => {
5538
5828
  g_currentPage = `option`;
5539
5829
  g_stateObj.filterKeys = ``;
5540
5830
 
5831
+ // 楽曲データの表示
5832
+ let text = `♪` + (g_headerObj.musicSelectUse ? `${g_headerObj.musicTitles[g_settings.musicIdxNum]} / ` : ``) +
5833
+ `BPM: ${g_headerObj.bpms[g_settings.musicIdxNum]}`;
5834
+ if (!g_headerObj.musicSelectUse && g_headerObj.bpms[g_settings.musicIdxNum] === `----`) {
5835
+ text = ``;
5836
+ }
5837
+ divRoot.appendChild(createDivCss2Label(`lblMusicInfo`, text,
5838
+ Object.assign({ siz: getFontSize(text, g_btnWidth(3 / 4), getBasicFont(), 12) }, g_lblPosObj.lblMusicInfo)));
5839
+
5541
5840
  // タイトル文字描画
5542
5841
  divRoot.appendChild(getTitleDivLabel(`lblTitle`, g_lblNameObj.settings, 0, 15, `settings_Title`));
5543
5842
 
@@ -6166,7 +6465,7 @@ const makeHighScore = _scoreId => {
6166
6465
 
6167
6466
  // 結果をクリップボードへコピー (ハイスコア保存分)
6168
6467
  if (g_localStorage.highscores?.[scoreName] !== undefined) {
6169
- const twiturl = new URL(g_localStorageUrl);
6468
+ const twiturl = new URL(g_localStorageUrlOrg);
6170
6469
  twiturl.searchParams.append(`scoreId`, _scoreId);
6171
6470
  const baseTwitUrl = g_isLocal ? `` : `${twiturl.toString()}`.replace(/[\t\n]/g, ``);
6172
6471
 
@@ -9144,6 +9443,7 @@ const scoreConvert = (_dosObj, _scoreId, _preblankFrame, _dummyNo = ``,
9144
9443
  const mergeColorData = (_header = ``) => {
9145
9444
  if (obj[`color${_header}Data`] === undefined) return [];
9146
9445
  const tmpArr = obj[`color${_header}Data`].concat(obj[`acolor${_header}Data`]);
9446
+ delete obj[`acolor${_header}Data`];
9147
9447
  return tmpArr.sort((_a, _b) => _a[0] - _b[0]).flat();
9148
9448
  };
9149
9449
 
@@ -10492,6 +10792,41 @@ const getArrowSettings = () => {
10492
10792
  g_workObj.dividePosDefault = g_workObj.dividePos.concat();
10493
10793
  g_stateObj.layerNum = Math.max(g_stateObj.layerNum, Math.ceil((Math.max(...g_workObj.dividePos) + 1) / 2) * 2);
10494
10794
 
10795
+ // g_workObjの不要なプロパティを削除
10796
+ if (g_stateObj.dummyId === ``) {
10797
+ Object.keys(g_workObj).filter(key => key.startsWith(`dummy`) || key.startsWith(`mkDummy`))
10798
+ .forEach(key => delete g_workObj[key]);
10799
+ }
10800
+ const targetColorKeys = [`mkColor`, `mkColorShadow`];
10801
+ const usedColorKeys = [];
10802
+ targetColorKeys.push(...g_typeLists.frzColor.map(type => `mkFColor${type}`));
10803
+ targetColorKeys.forEach(key => {
10804
+ if (g_workObj[key].length === 0) {
10805
+ delete g_workObj[key];
10806
+ delete g_workObj[`${key}Cd`];
10807
+ } else {
10808
+ usedColorKeys.push(key);
10809
+ }
10810
+ });
10811
+ [`Arrow`, `Step`].forEach(type => {
10812
+ [``, `Dir`, `Layer`].forEach(type2 => {
10813
+ if (g_workObj[`mkScrollch${type}${type2}`].length === 0) {
10814
+ delete g_workObj[`mkScrollch${type}${type2}`];
10815
+ }
10816
+ });
10817
+ });
10818
+ [`Arrow`, `Frz`].forEach(type => {
10819
+ if (g_workObj[`mk${type}ColorChangeAll`].length === 0) {
10820
+ delete g_workObj[`mk${type}ColorChangeAll`];
10821
+ }
10822
+ [``, `Name`].forEach(type2 => {
10823
+ if (g_workObj[`mk${type}CssMotion${type2}`].length === 0) {
10824
+ delete g_workObj[`mk${type}CssMotion${type2}`];
10825
+ }
10826
+ });
10827
+ });
10828
+
10829
+ // 初期位置、ライフ設定の初期化
10495
10830
  Object.keys(g_resultObj).forEach(judgeCnt => g_resultObj[judgeCnt] = 0);
10496
10831
  g_resultObj.spState = ``;
10497
10832
 
@@ -10593,7 +10928,7 @@ const getArrowSettings = () => {
10593
10928
  const _copiedArray = structuredClone(_array);
10594
10929
  return _array.map((_val, _i) => _array[_i] = randArray[_copiedArray[_i]]);
10595
10930
  };
10596
- [`mkColor`, `mkColorShadow`, `mkFColor`, `mkFColorShadow`].forEach(type => {
10931
+ usedColorKeys.forEach(type => {
10597
10932
  if (g_workObj[type] !== undefined) {
10598
10933
  for (let j = 0; j < g_workObj[type].length; j++) {
10599
10934
  if (g_workObj[type][j] === undefined) {
@@ -11219,7 +11554,7 @@ const mainInit = () => {
11219
11554
  * @param {string} _name 通常, ダミー
11220
11555
  */
11221
11556
  const changeArrowColor = (_j, _k, _name) => {
11222
- if (g_workObj[`mk${toCapitalize(_name)}ColorChangeAll`][g_scoreObj.frameNum]) {
11557
+ if (g_workObj[`mk${toCapitalize(_name)}ColorChangeAll`]?.[g_scoreObj.frameNum]) {
11223
11558
 
11224
11559
  /**
11225
11560
  * 全体色の変更処理
@@ -11257,7 +11592,7 @@ const mainInit = () => {
11257
11592
  */
11258
11593
  const changeFrzColor = (_j, _k, _name, _state) => {
11259
11594
 
11260
- if (g_workObj[`mk${toCapitalize(_name)}ColorChangeAll`][g_scoreObj.frameNum]) {
11595
+ if (g_workObj[`mk${toCapitalize(_name)}ColorChangeAll`]?.[g_scoreObj.frameNum]) {
11261
11596
  const frzNo = `${_j}_${_k}`;
11262
11597
  const frzTop = document.getElementById(`${_name}Top${frzNo}`);
11263
11598
  const frzBar = document.getElementById(`${_name}Bar${frzNo}`);
@@ -11864,13 +12199,13 @@ const mainInit = () => {
11864
12199
 
11865
12200
  // 個別・全体色変化 (矢印)
11866
12201
  g_typeLists.arrowColor.forEach(ctype =>
11867
- changeColors(g_workObj[`mk${headerU}Color${ctype}`][currentFrame],
11868
- g_workObj[`mk${headerU}Color${ctype}Cd`][currentFrame], header, `arrow${ctype}`));
12202
+ changeColors(g_workObj[`mk${headerU}Color${ctype}`]?.[currentFrame],
12203
+ g_workObj[`mk${headerU}Color${ctype}Cd`]?.[currentFrame], header, `arrow${ctype}`));
11869
12204
 
11870
12205
  // 個別・全体色変化(フリーズアロー)
11871
12206
  g_typeLists.frzColor.forEach(ctype =>
11872
- changeColors(g_workObj[`mk${headerU}FColor${ctype}`][currentFrame],
11873
- g_workObj[`mk${headerU}FColor${ctype}Cd`][currentFrame], header, `frz${ctype}`));
12207
+ changeColors(g_workObj[`mk${headerU}FColor${ctype}`]?.[currentFrame],
12208
+ g_workObj[`mk${headerU}FColor${ctype}Cd`]?.[currentFrame], header, `frz${ctype}`));
11874
12209
 
11875
12210
  // 矢印モーション
11876
12211
  changeCssMotions(header, `arrow`, currentFrame);
@@ -11893,7 +12228,7 @@ const mainInit = () => {
11893
12228
  changeStepY(currentFrame);
11894
12229
 
11895
12230
  // ダミー矢印生成(背面に表示するため先に処理)
11896
- g_workObj.mkDummyArrow[currentFrame]?.forEach(data =>
12231
+ g_workObj.mkDummyArrow?.[currentFrame]?.forEach(data =>
11897
12232
  makeArrow(data, ++dummyArrowCnts[data.pos], `dummyArrow`, g_workObj.dummyArrowColors[data.pos], g_workObj.dummyArrowShadowColors[data.pos]));
11898
12233
 
11899
12234
  // 矢印生成
@@ -11901,7 +12236,7 @@ const mainInit = () => {
11901
12236
  makeArrow(data, ++arrowCnts[data.pos], `arrow`, g_workObj.arrowColors[data.pos], g_workObj.arrowShadowColors[data.pos]));
11902
12237
 
11903
12238
  // ダミーフリーズアロー生成
11904
- g_workObj.mkDummyFrzArrow[currentFrame]?.forEach(data =>
12239
+ g_workObj.mkDummyFrzArrow?.[currentFrame]?.forEach(data =>
11905
12240
  makeFrzArrow(data, ++dummyFrzCnts[data.pos], `dummyFrz`, g_workObj.dummyFrzNormalColors[data.pos],
11906
12241
  g_workObj.dummyFrzNormalBarColors[data.pos], g_workObj.dummyFrzNormalShadowColors[data.pos]));
11907
12242
 
@@ -12316,7 +12651,7 @@ const changeColors = (_mkColor, _mkColorCd, _header, _name) => {
12316
12651
  */
12317
12652
  const changeCssMotions = (_header, _name, _frameNum) => {
12318
12653
  const camelHeader = _header === `` ? _name : `${_header}${toCapitalize(_name)}`;
12319
- g_workObj[`mk${toCapitalize(camelHeader)}CssMotion`][_frameNum]?.forEach((targetj, j) =>
12654
+ g_workObj[`mk${toCapitalize(camelHeader)}CssMotion`]?.[_frameNum]?.forEach((targetj, j) =>
12320
12655
  g_workObj[`${camelHeader}CssMotions`][targetj] =
12321
12656
  g_workObj[`mk${toCapitalize(camelHeader)}CssMotionName`][_frameNum][2 * j + (g_workObj.dividePos[targetj] % 2)]);
12322
12657
  };
@@ -12326,7 +12661,7 @@ const changeCssMotions = (_header, _name, _frameNum) => {
12326
12661
  * @param {number} _frameNum
12327
12662
  */
12328
12663
  const changeScrollArrowDirs = (_frameNum) =>
12329
- g_workObj.mkScrollchArrow[_frameNum]?.forEach((targetj, j) => {
12664
+ g_workObj.mkScrollchArrow?.[_frameNum]?.forEach((targetj, j) => {
12330
12665
  g_workObj.scrollDir[targetj] = g_workObj.scrollDirDefault[targetj] * g_workObj.mkScrollchArrowDir[_frameNum][j];
12331
12666
  const baseLayer = g_workObj.mkScrollchArrowLayer[_frameNum][j] === -1 ?
12332
12667
  Math.floor(g_workObj.dividePosDefault[targetj] / 2) : g_workObj.mkScrollchArrowLayer[_frameNum][j];
@@ -12338,7 +12673,7 @@ const changeScrollArrowDirs = (_frameNum) =>
12338
12673
  * @param {number} _frameNum
12339
12674
  */
12340
12675
  const changeStepY = (_frameNum) =>
12341
- g_workObj.mkScrollchStep[_frameNum]?.forEach((targetj, j) => {
12676
+ g_workObj.mkScrollchStep?.[_frameNum]?.forEach((targetj, j) => {
12342
12677
  const dividePos = (g_workObj.scrollDirDefault[targetj] * g_workObj.mkScrollchStepDir[_frameNum][j] === 1 ? 0 : 1);
12343
12678
 
12344
12679
  // 移動元のステップゾーンの不透明度、表示・非表示を退避
@@ -13398,7 +13733,7 @@ const resultInit = () => {
13398
13733
  }
13399
13734
  clearTimeout(g_timeoutEvtId);
13400
13735
  clearTimeout(g_timeoutEvtResultId);
13401
- }, Object.assign(_posObj, { resetFunc: _func }), _cssClass);
13736
+ }, Object.assign(_posObj, { resetFunc: () => _func() }), _cssClass);
13402
13737
 
13403
13738
  /**
13404
13739
  * 外部リンクボタンを作成
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * Source by tickle
7
7
  * Created : 2019/11/19
8
- * Revised : 2025/03/22 (v40.7.0)
8
+ * Revised : 2025/04/12 (v41.0.0)
9
9
  *
10
10
  * https://github.com/cwtickle/danoniplus
11
11
  */
@@ -191,6 +191,8 @@ const getScMsg = {
191
191
  */
192
192
  const updateWindowSiz = () => {
193
193
  Object.assign(g_windowObj, {
194
+ keyTitleSprite: { x: g_btnX(1 / 4), y: g_sHeight / 2 - 5, w: g_btnWidth(1 / 2), h: 16 },
195
+ mSelectTitleSprite: { x: g_btnX(), y: 0, w: g_btnWidth(), h: g_sHeight, clipPath: `inset(12% 0 8% 0)` },
194
196
  optionSprite: { x: (g_sWidth - 450) / 2, y: 65, w: 450, h: 325 },
195
197
  dataSprite: { x: g_btnX() + (g_sWidth - Math.max(g_sWidth - 100, 450)) / 2, y: 65, w: Math.max(g_sWidth - 100, 450), h: 325 },
196
198
  keyListSprite: { x: 0, y: g_limitObj.setLblHeight * 7.5 + 40, w: 150, h: g_sHeight - 380, overflow: C_DIS_AUTO },
@@ -242,6 +244,51 @@ const updateWindowSiz = () => {
242
244
  x: g_btnX(1) - 160, y: (g_sHeight / 2) + 150, w: 140, h: 50, siz: 20, border: `solid 1px #999999`,
243
245
  },
244
246
 
247
+ lblMusicSelect: {
248
+ x: g_btnX(1 / 4), y: g_sHeight / 2 - 90,
249
+ w: g_btnWidth(5 / 8), h: 206, siz: 14, border: `solid 1px #006666`,
250
+ align: C_ALIGN_LEFT, padding: `0 10px`, display: `inline-block`,
251
+ },
252
+ lblMusicSelectDetail: {
253
+ x: g_btnX(1 / 4), y: g_sHeight / 2 - 45,
254
+ w: g_btnWidth(5 / 8), h: 50, siz: 14,
255
+ align: C_ALIGN_LEFT, padding: `0 10px`, display: `inline-block`,
256
+ pointerEvents: C_DIS_INHERIT,
257
+ },
258
+ btnStart_music: {
259
+ x: g_btnX(27 / 32), y: g_sHeight / 2 - 90,
260
+ w: g_btnWidth(1 / 16), h: 206, siz: 24, padding: `0 10px`,
261
+ border: `solid 1px #006666`,
262
+ },
263
+ btnMusicSelectPrev: {
264
+ x: g_btnX(1 / 4), y: g_sHeight / 2 - 134,
265
+ w: 30, h: 40, siz: 20, padding: `0 10px`,
266
+ border: `solid 1px #666600`,
267
+ },
268
+ btnMusicSelectNext: {
269
+ x: g_btnX(1 / 4), y: g_sHeight / 2 + 120,
270
+ w: 30, h: 40, siz: 20, padding: `0 10px`,
271
+ border: `solid 1px #666600`,
272
+ },
273
+ btnMusicSelectRandom: {
274
+ x: g_btnX(1 / 4) - 80, y: g_sHeight / 2 - 134,
275
+ w: 55, h: 40, siz: 14, padding: `0 10px`,
276
+ border: `solid 1px #666666`,
277
+ },
278
+ btnKeyTitle: {
279
+ y: 0, w: 35, h: 16, siz: 14,
280
+ border: `solid 1px #666666`,
281
+ },
282
+ lblMusicCnt: {
283
+ x: g_btnX(1 / 4) - 80, y: g_sHeight / 2 - 90,
284
+ w: 80, h: 20, siz: 14, align: C_ALIGN_CENTER,
285
+ },
286
+ lblComment_music: {
287
+ x: g_btnX(1 / 4) + 10, y: g_sHeight / 2 + 15, w: g_btnWidth(7 / 12) - 5, h: 100,
288
+ siz: g_limitObj.difSelectorSiz, align: C_ALIGN_LEFT,
289
+ overflow: C_DIS_AUTO, whiteSpace: `normal`,
290
+ },
291
+
245
292
  /** データ管理 */
246
293
  btnResetBack: {
247
294
  x: g_btnX(), y: g_sHeight - 20, w: g_btnWidth(1 / 4), h: 16, siz: 12,
@@ -305,8 +352,11 @@ const updateWindowSiz = () => {
305
352
  title: g_msgObj.dataSave, borderStyle: `solid`,
306
353
  },
307
354
 
355
+ lblMusicInfo: {
356
+ x: g_btnX(1 / 4), y: 0, w: g_btnWidth(3 / 4), h: 20, align: C_ALIGN_RIGHT
357
+ },
308
358
  lblBaseSpd: {
309
- x: g_sWidth - 100, y: 0, w: 100, h: 20, siz: 14,
359
+ x: g_sWidth - 100, y: 11, w: 100, h: 20, siz: 13, align: C_ALIGN_RIGHT
310
360
  },
311
361
  btnReverse: {
312
362
  x: 160, y: 0, w: 90, h: 21, siz: g_limitObj.difSelectorSiz, borderStyle: `solid`,
@@ -948,6 +998,7 @@ let C_WOD_FRAME = 30;
948
998
 
949
999
  // 譜面データ持ち回り用
950
1000
  const g_stateObj = {
1001
+ keyInitial: false,
951
1002
  dosDivideFlg: false,
952
1003
  scoreLockFlg: false,
953
1004
  scoreId: 0,
@@ -1063,6 +1114,7 @@ const makeSpeedList = (_minSpd, _maxSpd) => [...Array((_maxSpd - _minSpd) * 20 +
1063
1114
  // 設定系全般管理
1064
1115
  const g_settings = {
1065
1116
 
1117
+ musicIdxNum: 0,
1066
1118
  dataMgtNum: {
1067
1119
  environment: 0,
1068
1120
  highscores: 0,
@@ -1073,6 +1125,8 @@ const g_settings = {
1073
1125
  keyStorages: [`reverse`, `keyCtrl`, `keyCtrlPtn`, `shuffle`, `color`, `stepRtn`],
1074
1126
  colorStorages: [`setColor`, `setShadowColor`, `frzColor`, `frzShadowColor`],
1075
1127
 
1128
+ mSelectableTerms: 3,
1129
+
1076
1130
  speeds: makeSpeedList(C_MIN_SPEED, C_MAX_SPEED),
1077
1131
  speedNum: 0,
1078
1132
  speedTerms: [20, 5, 1],
@@ -2019,6 +2073,8 @@ const g_shortcutObj = {
2019
2073
  ControlLeft_KeyC: { id: `` },
2020
2074
  KeyC: { id: `btnComment` },
2021
2075
  KeyD: { id: `btnReset` },
2076
+ ArrowUp: { id: `btnMusicSelectPrev` },
2077
+ ArrowDown: { id: `btnMusicSelectNext` },
2022
2078
  },
2023
2079
  dataMgt: {
2024
2080
  KeyE: { id: `btnEnvironment` },
@@ -3486,6 +3542,7 @@ const g_lang_msgInfoObj = {
3486
3542
  W_0021: `クリップボードのコピーに失敗しました。`,
3487
3543
  W_0031: `セーフモード適用中です。ローカルストレージ情報を使わない設定になっています。<br>
3488
3544
  「Data Management」から解除が可能です。(W-0031)`,
3545
+ W_0041: `選曲単品モードが有効になっています。<br><a href="{0}">[ 選曲画面へ戻る ]</a>`,
3489
3546
 
3490
3547
  E_0011: `アーティスト名が未入力です。(E-0011)`,
3491
3548
  E_0012: `曲名情報が未設定です。(E-0012)<br>
@@ -3541,6 +3598,7 @@ const g_lang_msgInfoObj = {
3541
3598
  W_0031: `Safe Mode is being applied. <br>
3542
3599
  The setting is set to not use local storage information <br>
3543
3600
  and can be removed from Data Management. (W-0031)`,
3601
+ W_0041: `The single music selection mode is enabled.<br><a href="{0}">[ Return to the original page ]</a>`,
3544
3602
 
3545
3603
  E_0011: `The artist name is not set. (E-0011)`,
3546
3604
  E_0012: `The song title information is not set. (E-0012)<br>
@@ -4175,6 +4233,7 @@ const g_customJsObj = {
4175
4233
  preTitle: [],
4176
4234
  title: [],
4177
4235
  titleEnterFrame: [],
4236
+ musicSelect: [],
4178
4237
  dataMgt: [],
4179
4238
  precondition: [],
4180
4239
  option: [],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "danoniplus",
3
- "version": "40.7.2",
3
+ "version": "41.0.1",
4
4
  "description": "Dancing☆Onigiri (CW Edition) - Web-based Rhythm Game",
5
5
  "main": "./js/danoni_main.js",
6
6
  "scripts": {