danoniplus 46.4.0 → 46.5.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.
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Dancing☆Onigiri (CW Edition)
2
2
 
3
3
  [![CodeQL](https://github.com/cwtickle/danoniplus/workflows/CodeQL/badge.svg)](https://github.com/cwtickle/danoniplus/actions?query=workflow%3ACodeQL)
4
- ![CodeRabbit Pull Request Reviews](https://img.shields.io/coderabbit/prs/github/cwtickle/danoniplus?utm_source=oss&utm_medium=github&utm_campaign=cwtickle%2Fdanoniplus&labelColor=171717&color=FF570A&link=https%3A%2F%2Fcoderabbit.ai&label=CodeRabbit+Reviews&logo=coderabbit)
4
+ [![CodeRabbit Pull Request Reviews](https://img.shields.io/coderabbit/prs/github/cwtickle/danoniplus?utm_source=oss&utm_medium=github&utm_campaign=cwtickle%2Fdanoniplus&labelColor=171717&color=FF570A&link=https%3A%2F%2Fcoderabbit.ai&label=CodeRabbit+Reviews&logo=coderabbit)](https://www.coderabbit.ai/)
5
5
  [![Discord](https://img.shields.io/discord/698460971231870977?style=flat&logo=discord&logoColor=%23ffffff&label=Discord&labelColor=%236666ff&color=%23000066)](https://discord.gg/YVWUdUGyMy)
6
6
  [![DeepWiki](https://img.shields.io/badge/DeepWiki-cwtickle%2Fdanoniplus-blue.svg?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAyCAYAAAAnWDnqAAAAAXNSR0IArs4c6QAAA05JREFUaEPtmUtyEzEQhtWTQyQLHNak2AB7ZnyXZMEjXMGeK/AIi+QuHrMnbChYY7MIh8g01fJoopFb0uhhEqqcbWTp06/uv1saEDv4O3n3dV60RfP947Mm9/SQc0ICFQgzfc4CYZoTPAswgSJCCUJUnAAoRHOAUOcATwbmVLWdGoH//PB8mnKqScAhsD0kYP3j/Yt5LPQe2KvcXmGvRHcDnpxfL2zOYJ1mFwrryWTz0advv1Ut4CJgf5uhDuDj5eUcAUoahrdY/56ebRWeraTjMt/00Sh3UDtjgHtQNHwcRGOC98BJEAEymycmYcWwOprTgcB6VZ5JK5TAJ+fXGLBm3FDAmn6oPPjR4rKCAoJCal2eAiQp2x0vxTPB3ALO2CRkwmDy5WohzBDwSEFKRwPbknEggCPB/imwrycgxX2NzoMCHhPkDwqYMr9tRcP5qNrMZHkVnOjRMWwLCcr8ohBVb1OMjxLwGCvjTikrsBOiA6fNyCrm8V1rP93iVPpwaE+gO0SsWmPiXB+jikdf6SizrT5qKasx5j8ABbHpFTx+vFXp9EnYQmLx02h1QTTrl6eDqxLnGjporxl3NL3agEvXdT0WmEost648sQOYAeJS9Q7bfUVoMGnjo4AZdUMQku50McDcMWcBPvr0SzbTAFDfvJqwLzgxwATnCgnp4wDl6Aa+Ax283gghmj+vj7feE2KBBRMW3FzOpLOADl0Isb5587h/U4gGvkt5v60Z1VLG8BhYjbzRwyQZemwAd6cCR5/XFWLYZRIMpX39AR0tjaGGiGzLVyhse5C9RKC6ai42ppWPKiBagOvaYk8lO7DajerabOZP46Lby5wKjw1HCRx7p9sVMOWGzb/vA1hwiWc6jm3MvQDTogQkiqIhJV0nBQBTU+3okKCFDy9WwferkHjtxib7t3xIUQtHxnIwtx4mpg26/HfwVNVDb4oI9RHmx5WGelRVlrtiw43zboCLaxv46AZeB3IlTkwouebTr1y2NjSpHz68WNFjHvupy3q8TFn3Hos2IAk4Ju5dCo8B3wP7VPr/FGaKiG+T+v+TQqIrOqMTL1VdWV1DdmcbO8KXBz6esmYWYKPwDL5b5FA1a0hwapHiom0r/cKaoqr+27/XcrS5UwSMbQAAAABJRU5ErkJggg==)](https://deepwiki.com/cwtickle/danoniplus)
7
7
  [![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/cwtickle/danoniplus?sort=semver)](https://github.com/cwtickle/danoniplus/security/policy)
@@ -68,6 +68,7 @@ If the life of the life gauge remains by the end of the game or it is over the q
68
68
  ## How to Make / 作り方
69
69
 
70
70
  - [How to make chart overview](https://github.com/cwtickle/danoniplus-docs/wiki/HowtoMake) / [譜面の作成概要](https://github.com/cwtickle/danoniplus/wiki/HowtoMake)
71
+ - [Creating chart Using the Preview Site](https://github.com/cwtickle/danoniplus-docs/wiki/HowToUsePreview-Example) / [プレビューサイトを使った譜面の作成](https://github.com/cwtickle/danoniplus/wiki/HowToUsePreview-Example)
71
72
  - [ParaFla!ソース利用者向け移行方法 (Japanese Only)](https://github.com/cwtickle/danoniplus/wiki/forParaFlaUser)
72
73
 
73
74
  ### How to Install / 導入方法
@@ -120,7 +121,7 @@ If you would like to cooperate with the development, please see below. Even if y
120
121
 
121
122
  ### Dancing☆Onigiri
122
123
 
123
- - [Dancing☆Onigiri Preview](https://danonicw.skr.jp/)
124
+ - [Dancing☆Onigiri Preview](https://danonicw.skr.jp/) ([Repository](https://github.com/cwtickle/danoniplus-preview)) @cwtickle
124
125
  - [Dancing☆Onigiri エディター(CW Edition 対応)](https://github.com/superkuppabros/danoni-editor) @superkuppabros
125
126
  - [ダンおに曲データjs化ツール](https://github.com/suzme/danoni-base64) @suzme
126
127
  - [ダンおに矢印色ツール](https://github.com/suzme/danoni-colorpicker) @suzme
@@ -134,6 +135,7 @@ If you would like to cooperate with the development, please see below. Even if y
134
135
 
135
136
  ### Kirizma / キリズマ
136
137
 
138
+ - [Kirizma (CW Edition)](https://github.com/cwtickle/kirizma-cw) @cwtickle
137
139
  - [キリズマ譜面データ変換機](https://github.com/suzme/kirizma-converter) @suzme
138
140
  - [キリズマ歌詞表示作成ツール](https://github.com/prlg25/kirizma_lyric) @prlg25
139
141
  - [キリズマ難易度表](https://github.com/suzme/kirizma) @suzme
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 : 2026/03/26
7
+ * Revised : 2026/03/29
8
8
  *
9
9
  * https://github.com/cwtickle/danoniplus
10
10
  */
11
- const g_version = `Ver 46.4.0`;
12
- const g_revisedDate = `2026/03/26`;
11
+ const g_version = `Ver 46.5.1`;
12
+ const g_revisedDate = `2026/03/30`;
13
13
 
14
14
  // カスタム用バージョン (danoni_custom.js 等で指定可)
15
15
  let g_localVersion = ``;
@@ -919,7 +919,7 @@ const blockCode = _setCode => !C_BLOCK_KEYS.includes(_setCode);
919
919
  * キーを押したときの動作(汎用)
920
920
  * @param {KeyboardEvent} _evt
921
921
  * @param {string} _displayName
922
- * @param {function} _func
922
+ * @param {Function} _func
923
923
  * @param {boolean} _dfEvtFlg
924
924
  * @returns {boolean}
925
925
  */
@@ -1006,7 +1006,7 @@ const createScTextCommon = _displayName => {
1006
1006
  /**
1007
1007
  * ショートカットキー有効化
1008
1008
  * @param {string} _displayName
1009
- * @param {function} _func
1009
+ * @param {Function} _func
1010
1010
  * @param {boolean} [object.displayFlg=true]
1011
1011
  * @param {boolean} [object.dfEvtFlg=false]
1012
1012
  */
@@ -1176,6 +1176,43 @@ const loadMultipleFiles2 = async (_fileData, _loadType) => {
1176
1176
  }));
1177
1177
  };
1178
1178
 
1179
+ /**
1180
+ * ユーザー定義のカスタム関数配列を安全に実行する共通関数
1181
+ * @param {string} _hookName - 識別名
1182
+ * @param {Function[]} _funcArray - カスタム関数の配列
1183
+ * @param {...any} args - 関数に渡したい引数(可変長)
1184
+ */
1185
+ const safeExecuteCustomHooks = (_hookName, _funcArray, ...args) => {
1186
+ if (!Array.isArray(_funcArray)) return true;
1187
+ const errorCache = g_errorCache[_hookName];
1188
+
1189
+ for (const [index, func] of _funcArray.entries()) {
1190
+ if (typeof func !== C_TYP_FUNCTION) continue; // 関数以外が入っていた場合の自衛
1191
+ if (errorCache && errorCache[index]?.has(func)) continue; // エラー検知済みの場合は以後スキップ
1192
+
1193
+ try {
1194
+ // ...args で受け取った引数をそのまま横流しして実行
1195
+ func(...args);
1196
+ } catch (e) {
1197
+
1198
+ if (!errorCache) {
1199
+ // ループしない場合のエラー処理
1200
+ console.group(`${unEscapeEmoji(g_emojiObj.crossMark)} Custom Function Error: [${_hookName}] (Index: ${index}${func.name ? `, Func: ${func.name}` : ``})`);
1201
+ console.error(e);
1202
+ console.groupEnd();
1203
+
1204
+ } else if (errorCache && !errorCache[index]?.has(func)) {
1205
+ // ループがある場合のエラー処理(初回のみ)
1206
+ console.group(`${unEscapeEmoji(g_emojiObj.crossMark)} Custom Function Error: [${_hookName}] (Index: ${index}${func.name ? `, Func: ${func.name}` : ``})`);
1207
+ console.error(`${unEscapeEmoji(g_emojiObj.policeLight)} [${g_scoreObj.baseFrame} Frame] ${g_msgObj.customFunctionError}`, e);
1208
+ console.groupEnd();
1209
+ errorCache[index] = new Set();
1210
+ errorCache[index].add(func);
1211
+ }
1212
+ }
1213
+ }
1214
+ }
1215
+
1179
1216
  /**
1180
1217
  * 与えられたパスより、キーワードとディレクトリに分割
1181
1218
  * @param {string} _fileName
@@ -1589,7 +1626,7 @@ const createImg = (_id, _imgPath, _x, _y, _width, _height) => {
1589
1626
  * ColorPickerの作成
1590
1627
  * @param {string} _parentObj
1591
1628
  * @param {string} _id
1592
- * @param {function} _func
1629
+ * @param {Function} _func
1593
1630
  * @param {number} [object.x=0]
1594
1631
  * @param {number} [object.y=0]
1595
1632
  * @returns {HTMLInputElement}
@@ -1772,7 +1809,7 @@ const deleteDiv = (_parentId, _idName) => {
1772
1809
  * ボタンの作成 (CSS版・拡張属性対応)
1773
1810
  * @param {string} _id
1774
1811
  * @param {string} _text
1775
- * @param {function} _func
1812
+ * @param {Function} _func
1776
1813
  * @param {number} [object.x]
1777
1814
  * @param {number} [object.y]
1778
1815
  * @param {number} [object.w=g_btnWidth() / 3]
@@ -1782,8 +1819,8 @@ const deleteDiv = (_parentId, _idName) => {
1782
1819
  * @param {string} [object.title] ボタンオンマウス時のコメント
1783
1820
  * @param {string} [object.groupName] 画面名 (g_btnWaitFrameで定義しているプロパティ名を指定)
1784
1821
  * @param {boolean} [object.initDisabledFlg=true] ボタン有効化までの時間を設けるかどうか
1785
- * @param {function} [object.resetFunc] カスタム処理後に実行する処理
1786
- * @param {function} [object.cxtFunc] 右クリック時に実行する処理
1822
+ * @param {Function} [object.resetFunc] カスタム処理後に実行する処理
1823
+ * @param {Function} [object.cxtFunc] 右クリック時に実行する処理
1787
1824
  * @param {...any} [object.rest]
1788
1825
  * @param {...any} _classes
1789
1826
  */
@@ -2143,7 +2180,7 @@ const checkDuplicatedObjects = _obj => {
2143
2180
  /**
2144
2181
  * 多層スプライトデータの作成処理
2145
2182
  * @param {string} _data
2146
- * @param {function} _calcFrame
2183
+ * @param {Function} _calcFrame
2147
2184
  * @returns [多層スプライトデータ, 最大深度]
2148
2185
  */
2149
2186
  const makeSpriteData = (_data, _calcFrame = _frame => _frame) => {
@@ -2233,7 +2270,7 @@ const makeSpriteData = (_data, _calcFrame = _frame => _frame) => {
2233
2270
  /**
2234
2271
  * スタイル変更データの作成処理
2235
2272
  * @param {string} _data
2236
- * @param {function} _calcFrame
2273
+ * @param {Function} _calcFrame
2237
2274
  * @returns [多層スプライトデータ, 1(固定)]
2238
2275
  */
2239
2276
  const makeStyleData = (_data, _calcFrame = _frame => _frame) => {
@@ -2781,7 +2818,7 @@ const initialControl = async () => {
2781
2818
  }
2782
2819
  }
2783
2820
  }
2784
- g_customJsObj.preTitle.forEach(func => func());
2821
+ safeExecuteCustomHooks(`g_customJsObj.preTitle`, g_customJsObj.preTitle);
2785
2822
  const queryMusicId = getQueryParamVal(`musicId`);
2786
2823
  g_settings.musicIdxNum = queryMusicId !== null ? Number(queryMusicId) :
2787
2824
  g_headerObj.musicGroups?.[g_headerObj.musicNos[g_stateObj.scoreId]] ??
@@ -4840,7 +4877,7 @@ const keysConvert = (_dosObj, { keyExtraList = _dosObj.keyExtraList?.split(`,`)
4840
4877
  * - charaX の場合に限り、a>5_0 の形式を aleft, adown, aup, aright, aspace に変換する
4841
4878
  * @param {string} _str
4842
4879
  * @param {string} _name
4843
- * @param {function} _convFunc
4880
+ * @param {Function} _convFunc
4844
4881
  * @returns {string[]|number[]}
4845
4882
  */
4846
4883
  const expandKeyPtn = (_str, _name, _convFunc) => {
@@ -4859,10 +4896,10 @@ const keysConvert = (_dosObj, { keyExtraList = _dosObj.keyExtraList?.split(`,`)
4859
4896
  * 新キー用複合パラメータ
4860
4897
  * @param {string} _key キー数
4861
4898
  * @param {string} _name 名前
4862
- * @param {function} _convFunc マッピング関数
4899
+ * @param {Function} _convFunc マッピング関数
4863
4900
  * @param {string} object.errCd エラーコード
4864
4901
  * @param {boolean} object.baseCopyFlg コピー配列の準備可否
4865
- * @param {function} object.loopFunc パターン別に処理する個別関数
4902
+ * @param {Function} object.loopFunc パターン別に処理する個別関数
4866
4903
  * @returns {number} 最小パターン数
4867
4904
  */
4868
4905
  const newKeyMultiParam = (_key, _name, _convFunc, { errCd = ``, baseCopyFlg = false, loopFunc = () => true } = {}) => {
@@ -5395,7 +5432,7 @@ const titleInit = (_initFlg = false) => {
5395
5432
  }
5396
5433
 
5397
5434
  // ユーザカスタムイベント(初期)
5398
- g_customJsObj.title.forEach(func => func());
5435
+ safeExecuteCustomHooks(`g_customJsObj.title`, g_customJsObj.title);
5399
5436
 
5400
5437
  // バージョン情報取得
5401
5438
  let customVersion = ``;
@@ -5522,7 +5559,7 @@ const titleInit = (_initFlg = false) => {
5522
5559
  const flowTitleTimeline = () => {
5523
5560
 
5524
5561
  // ユーザカスタムイベント(フレーム毎)
5525
- g_customJsObj.titleEnterFrame.forEach(func => func());
5562
+ safeExecuteCustomHooks(`g_customJsObj.titleEnterFrame`, g_customJsObj.titleEnterFrame);
5526
5563
 
5527
5564
  // 背景・マスクモーション、スキン変更
5528
5565
  drawTitleResultMotion(g_currentPage);
@@ -5543,7 +5580,7 @@ const titleInit = (_initFlg = false) => {
5543
5580
  document.oncontextmenu = () => true;
5544
5581
  divRoot.oncontextmenu = () => false;
5545
5582
 
5546
- g_skinJsObj.title.forEach(func => func());
5583
+ safeExecuteCustomHooks(`g_skinJsObj.title`, g_skinJsObj.title);
5547
5584
  };
5548
5585
 
5549
5586
  /**
@@ -5703,8 +5740,8 @@ const playBGM = async (_num, _currentLoopNum = g_settings.musicLoopNum) => {
5703
5740
  * @param {number} startVolume - 開始音量 (0〜1)
5704
5741
  * @param {number} endVolume - 終了音量 (0〜1)
5705
5742
  * @param {number} step - 1ステップの増減量
5706
- * @param {function} onEnd - フェード完了時の処理
5707
- * @param {function} isValid - フェード継続条件(true: 継続, false: 中断)
5743
+ * @param {Function} onEnd - フェード完了時の処理
5744
+ * @param {Function} isValid - フェード継続条件(true: 継続, false: 中断)
5708
5745
  * @returns {number} timeoutId
5709
5746
  */
5710
5747
  const fadeVolume = (startVolume, endVolume, step, onEnd, isValid) => {
@@ -5750,9 +5787,9 @@ const playBGM = async (_num, _currentLoopNum = g_settings.musicLoopNum) => {
5750
5787
 
5751
5788
  /**
5752
5789
  * 汎用ポーリング(監視)処理
5753
- * @param {function} check - 条件チェック関数(true なら終了)
5754
- * @param {function} onEnd - 終了時の処理
5755
- * @param {function} isValid - 継続条件(true: 継続, false: 中断)
5790
+ * @param {Function} check - 条件チェック関数(true なら終了)
5791
+ * @param {Function} onEnd - 終了時の処理
5792
+ * @param {Function} isValid - 継続条件(true: 継続, false: 中断)
5756
5793
  * @returns {number} timeoutId
5757
5794
  */
5758
5795
  const poll = (check, onEnd, isValid) => {
@@ -6038,7 +6075,7 @@ const changeMSelect = (_num, _initFlg = false) => {
6038
6075
  }
6039
6076
 
6040
6077
  // 選曲変更時のカスタム関数実行
6041
- g_customJsObj.musicSelect.forEach(func => func(g_settings.musicIdxNum));
6078
+ safeExecuteCustomHooks(`g_customJsObj.musicSelect`, g_customJsObj.musicSelect, g_settings.musicIdxNum);
6042
6079
  };
6043
6080
 
6044
6081
  /**
@@ -6179,7 +6216,7 @@ const dataMgtInit = () => {
6179
6216
  * @param {number} _heightPos
6180
6217
  * @param {number} _widthPos
6181
6218
  * @param {number} [w=125]
6182
- * @param {function} func
6219
+ * @param {Function} func
6183
6220
  * @returns {HTMLDivElement}
6184
6221
  */
6185
6222
  const createMgtButton = (_name, _heightPos, _widthPos, { w = 125, func = () => true, ...rest } = {}) => {
@@ -6268,7 +6305,7 @@ const dataMgtInit = () => {
6268
6305
  document.getElementById(`btnView${selectedKey}`).click();
6269
6306
 
6270
6307
  // ユーザカスタムイベント(初期)
6271
- g_customJsObj.dataMgt.forEach(func => func());
6308
+ safeExecuteCustomHooks(`g_customJsObj.dataMgt`, g_customJsObj.dataMgt);
6272
6309
 
6273
6310
  multiAppend(divRoot,
6274
6311
  createCss2Button(`btnBack`, g_lblNameObj.b_back, () => true, {
@@ -6360,7 +6397,7 @@ const dataMgtInit = () => {
6360
6397
  document.oncontextmenu = () => true;
6361
6398
  divRoot.oncontextmenu = () => false;
6362
6399
 
6363
- g_skinJsObj.dataMgt.forEach(func => func());
6400
+ safeExecuteCustomHooks(`g_skinJsObj.dataMgt`, g_skinJsObj.dataMgt);
6364
6401
  };
6365
6402
 
6366
6403
 
@@ -6438,7 +6475,7 @@ const preconditionInit = () => {
6438
6475
  }, g_cssObj.button_Setting));
6439
6476
 
6440
6477
  // ユーザカスタムイベント(初期)
6441
- g_customJsObj.precondition.forEach(func => func());
6478
+ safeExecuteCustomHooks(`g_customJsObj.precondition`, g_customJsObj.precondition);
6442
6479
 
6443
6480
  multiAppend(divRoot,
6444
6481
 
@@ -6461,7 +6498,7 @@ const preconditionInit = () => {
6461
6498
  document.oncontextmenu = () => true;
6462
6499
  divRoot.oncontextmenu = () => true;
6463
6500
 
6464
- g_skinJsObj.precondition.forEach(func => func());
6501
+ safeExecuteCustomHooks(`g_skinJsObj.precondition`, g_skinJsObj.precondition);
6465
6502
  };
6466
6503
 
6467
6504
  /*-----------------------------------------------------------*/
@@ -6580,12 +6617,12 @@ const updateSettingSummary = () => {
6580
6617
  `(Adj: ${g_stateObj.adjustment} f, Volume: ${g_stateObj.volume}%, ` +
6581
6618
  `ColorType: ${g_colorType}, KeyPattern: ${g_keyObj.currentPtn === -1 ? 'Self' : g_keyObj.currentPtn + 1})`;
6582
6619
 
6583
- g_customJsObj.settingSummary.forEach(func => func());
6620
+ safeExecuteCustomHooks(`g_customJsObj.settingSummary`, g_customJsObj.settingSummary);
6584
6621
  };
6585
6622
 
6586
6623
  /**
6587
6624
  * PLAYボタンの作成
6588
- * @param {function} _func
6625
+ * @param {Function} _func
6589
6626
  * @returns {HTMLDivElement}
6590
6627
  */
6591
6628
  const makePlayButton = _func => createCss2Button(`btnPlay`, g_lblNameObj.b_play, () => true, {
@@ -6615,7 +6652,7 @@ const optionInit = () => {
6615
6652
  createOptionWindow(divRoot);
6616
6653
 
6617
6654
  // ユーザカスタムイベント(初期)
6618
- g_customJsObj.option.forEach(func => func());
6655
+ safeExecuteCustomHooks(`g_customJsObj.option`, g_customJsObj.option);
6619
6656
 
6620
6657
  // ボタン描画
6621
6658
  commonSettingBtn(`Display`);
@@ -6625,7 +6662,7 @@ const optionInit = () => {
6625
6662
  document.oncontextmenu = () => true;
6626
6663
  g_initialFlg = true;
6627
6664
 
6628
- g_skinJsObj.option.forEach(func => func());
6665
+ safeExecuteCustomHooks(`g_skinJsObj.option`, g_skinJsObj.option);
6629
6666
  };
6630
6667
 
6631
6668
  /**
@@ -7600,7 +7637,7 @@ const setDifficulty = (_initFlg) => {
7600
7637
  lblMusicInfo.style.fontSize = wUnit(getFontSize2(lblMusicInfo.textContent, g_btnWidth(3 / 4), { maxSiz: 12 }));
7601
7638
 
7602
7639
  // ユーザカスタムイベント(初期)
7603
- g_customJsObj.difficulty.forEach(func => func(_initFlg, g_canLoadDifInfoFlg));
7640
+ safeExecuteCustomHooks(`g_customJsObj.difficulty`, g_customJsObj.difficulty, _initFlg, g_canLoadDifInfoFlg);
7604
7641
 
7605
7642
  // 設定サマリー表示の更新
7606
7643
  updateSettingSummary();
@@ -7927,8 +7964,8 @@ const createOptionWindow = _sprite => {
7927
7964
  * @param {string} [object.unitName=''] 設定名の単位
7928
7965
  * @param {number[]} [object.skipTerms] ボタンの設定スキップ間隔(デフォルト:[1(外側), 1(内側), 1(最内側)])
7929
7966
  * @param {boolean} [object.hiddenBtn=false] 隠しボタン(ショートカットキーのみ)の利用有無
7930
- * @param {function} [object.addRFunc] 右側のボタンを押したときの追加処理
7931
- * @param {function} [object.addLFunc] 左側のボタンを押したときの追加処理
7967
+ * @param {Function} [object.addRFunc] 右側のボタンを押したときの追加処理
7968
+ * @param {Function} [object.addLFunc] 左側のボタンを押したときの追加処理
7932
7969
  * @param {string} [object.settingLabel=_settingName] 設定名
7933
7970
  * @param {string} [object.displayName] 画面名
7934
7971
  * @param {string} [object.scLabel=''] ショートカットキーの表示名
@@ -8034,7 +8071,7 @@ const getStgDetailName = _name => {
8034
8071
  * 設定メイン・汎用
8035
8072
  * @param {number} _scrollNum
8036
8073
  * @param {string} _settingName
8037
- * @param {function} [func=()=>true] 設定ボタンを押した後の追加処理
8074
+ * @param {Function} [func=()=>true] 設定ボタンを押した後の追加処理
8038
8075
  * @param {string} [unitName=''] 設定の単位名
8039
8076
  * @param {number} [roundNum=0] 設定スキップ間隔の丸め基準数
8040
8077
  */
@@ -8398,13 +8435,13 @@ const getKeyCtrl = (_localStorage, _extraKeyName = ``) => {
8398
8435
  * @param {string} _id
8399
8436
  * @param {string} _name 初期設定文字
8400
8437
  * @param {number} _heightPos 上からの配置順
8401
- * @param {function} _func 通常ボタン処理
8438
+ * @param {Function} _func 通常ボタン処理
8402
8439
  * @param {number} [object.x]
8403
8440
  * @param {number} [object.y]
8404
8441
  * @param {number} [object.w]
8405
8442
  * @param {number} [object.h]
8406
8443
  * @param {number} [object.siz]
8407
- * @param {function} [object.cxtFunc] 右クリック時の処理
8444
+ * @param {Function} [object.cxtFunc] 右クリック時の処理
8408
8445
  * @param {...any} [object.rest]
8409
8446
  * @param {...any} _classes 追加するクラス
8410
8447
  * @returns {HTMLDivElement}
@@ -8420,7 +8457,7 @@ const makeSettingLblCssButton = (_id, _name, _heightPos, _func, {
8420
8457
  * @param {string} _id
8421
8458
  * @param {string} _name 初期設定文字
8422
8459
  * @param {number} _heightPos 上からの配置順
8423
- * @param {function} _func
8460
+ * @param {Function} _func
8424
8461
  * @param {number} [object.x]
8425
8462
  * @param {number} [object.h]
8426
8463
  * @param {number} [object.y=h*_heightPos]
@@ -8441,7 +8478,7 @@ const makeDifLblCssButton = (_id, _name, _heightPos, _func, {
8441
8478
  * @param {string} _id
8442
8479
  * @param {string} _directionFlg 表示用ボタンのどちら側に置くかを設定。(R, RR:右、L, LL:左)
8443
8480
  * @param {number} _heightPos 上からの配置順
8444
- * @param {function} _func
8481
+ * @param {Function} _func
8445
8482
  * @param {number} [object.dx=0]
8446
8483
  * @param {number} [object.dy=0]
8447
8484
  * @param {number} [object.dw=0]
@@ -8498,7 +8535,7 @@ const settingsDisplayInit = () => {
8498
8535
  divRoot.appendChild(createDescDiv(`scMsg`, g_lblNameObj.sdShortcutDesc));
8499
8536
 
8500
8537
  // ユーザカスタムイベント(初期)
8501
- g_customJsObj.settingsDisplay.forEach(func => func());
8538
+ safeExecuteCustomHooks(`g_customJsObj.settingsDisplay`, g_customJsObj.settingsDisplay);
8502
8539
 
8503
8540
  // ボタン描画
8504
8541
  commonSettingBtn(`Settings`);
@@ -8507,7 +8544,7 @@ const settingsDisplayInit = () => {
8507
8544
  setShortcutEvent(g_currentPage);
8508
8545
  document.oncontextmenu = () => true;
8509
8546
 
8510
- g_skinJsObj.settingsDisplay.forEach(func => func());
8547
+ safeExecuteCustomHooks(`g_skinJsObj.settingsDisplay`, g_skinJsObj.settingsDisplay);
8511
8548
  };
8512
8549
 
8513
8550
  /**
@@ -8762,7 +8799,7 @@ const exSettingInit = () => {
8762
8799
  );
8763
8800
 
8764
8801
  // ユーザカスタムイベント(初期)
8765
- g_customJsObj.exSetting.forEach(func => func());
8802
+ safeExecuteCustomHooks(`g_customJsObj.exSetting`, g_customJsObj.exSetting);
8766
8803
 
8767
8804
  // 設定系のボタン群をまとめて作成(Data Save, Display切替, Back, KeyConfig, Playボタン)
8768
8805
  commonSettingBtn(g_currentPage);
@@ -8771,18 +8808,18 @@ const exSettingInit = () => {
8771
8808
  setShortcutEvent(g_currentPage, () => true, { dfEvtFlg: true });
8772
8809
  document.oncontextmenu = () => true;
8773
8810
 
8774
- g_skinJsObj.exSetting.forEach(func => func());
8811
+ safeExecuteCustomHooks(`g_skinJsObj.exSetting`, g_skinJsObj.exSetting);
8775
8812
  };
8776
8813
 
8777
8814
  /**
8778
8815
  * 拡張設定込みの標準設定
8779
8816
  * @param {any[]} _spriteList
8780
8817
  * @param {string} _name
8781
- * @param {{ defaultList?: string[], displayName?: string, func?: function, funcEx?: function }} [options={}]
8818
+ * @param {{ defaultList?: string[], displayName?: string, func?: Function, funcEx?: Function }} [options={}]
8782
8819
  * @param {string[]} [options.defaultList=[C_FLG_OFF]] 拡張設定未使用の設定リスト
8783
8820
  * @param {string} [options.displayName='exSetting']
8784
- * @param {function} [options.func=()=>true] 通常ボタン用追加関数
8785
- * @param {function} [options.funcEx=()=>true] 拡張ボタン用追加関数
8821
+ * @param {Function} [options.func=()=>true] 通常ボタン用追加関数
8822
+ * @param {Function} [options.funcEx=()=>true] 拡張ボタン用追加関数
8786
8823
  */
8787
8824
  const createGeneralSettingEx = (_spriteList, _name, { defaultList = [C_FLG_OFF], displayName = `exSetting`,
8788
8825
  func = () => true, funcEx = () => true } = {}) => {
@@ -9141,14 +9178,14 @@ const keyConfigInit = (_kcType = g_kcType) => {
9141
9178
  * キーコンフィグ用設定ボタン
9142
9179
  * @param {string} _id
9143
9180
  * @param {string} _text
9144
- * @param {function} _func
9181
+ * @param {Function} _func
9145
9182
  * @param {number} [object.x=g_btnX(5 / 6) - 20]
9146
9183
  * @param {number} [object.y=15]
9147
9184
  * @param {number} [object.w=g_btnWidth(1 / 6)]
9148
9185
  * @param {number} [object.h=18]
9149
9186
  * @param {number} [object.siz=g_limitObj.jdgCntsSiz]
9150
9187
  * @param {string} [object.borderStyle='solid']
9151
- * @param {function} [object.cxtFunc]
9188
+ * @param {Function} [object.cxtFunc]
9152
9189
  * @param {...any} [object.rest]
9153
9190
  * @param {string} [_mainClass=g_cssObj.button_RevOFF]
9154
9191
  * @param {...any} _classes
@@ -9162,7 +9199,7 @@ const keyConfigInit = (_kcType = g_kcType) => {
9162
9199
  * キーコンフィグ用ミニボタン
9163
9200
  * @param {string} _id
9164
9201
  * @param {string} _directionFlg
9165
- * @param {function} _func
9202
+ * @param {Function} _func
9166
9203
  * @param {number} [object.x=g_btnX(5 / 6) - 30]
9167
9204
  * @param {number} [object.y=15]
9168
9205
  * @param {number} [object.w=15]
@@ -9492,7 +9529,7 @@ const keyConfigInit = (_kcType = g_kcType) => {
9492
9529
  * ColorPicker部分の作成
9493
9530
  * @param {number} _j
9494
9531
  * @param {string} _type
9495
- * @param {function} _func
9532
+ * @param {Function} _func
9496
9533
  * @param {number} [object.x=0]
9497
9534
  * @param {number} [object.y=15]
9498
9535
  */
@@ -9594,7 +9631,7 @@ const keyConfigInit = (_kcType = g_kcType) => {
9594
9631
  };
9595
9632
 
9596
9633
  // ユーザカスタムイベント(初期)
9597
- g_customJsObj.keyconfig.forEach(func => func());
9634
+ safeExecuteCustomHooks(`g_customJsObj.keyconfig`, g_customJsObj.keyconfig);
9598
9635
 
9599
9636
  // 部分キー表示用ボタン描画
9600
9637
  if (configKeyGroupList.length > 1) {
@@ -9671,12 +9708,15 @@ const keyConfigInit = (_kcType = g_kcType) => {
9671
9708
  const keyCdObj = document.getElementById(`keycon${g_currentj}_${g_currentk}`);
9672
9709
  let setKey = g_kCdN.findIndex(kCd => kCd === kbCode);
9673
9710
 
9711
+ const C_KEY_ESCAPE = 27;
9712
+ const C_KEY_IME = 229;
9713
+
9674
9714
  // 全角切替、BackSpace、Deleteキー、Escキーは割り当て禁止
9675
9715
  // また、直前と同じキーを押した場合(BackSpaceを除く)はキー操作を無効にする
9676
- const disabledKeys = [240, 242, 243, 244, 91, 29, 28, 27, 259, g_prevKey];
9716
+ const disabledKeys = [240, 242, 243, 244, 91, 29, 28, 259, g_prevKey];
9677
9717
 
9678
9718
  if (g_localeObj.val === `Ja`) {
9679
- disabledKeys.unshift(229);
9719
+ disabledKeys.unshift(C_KEY_IME);
9680
9720
  }
9681
9721
  if (disabledKeys.includes(setKey) || g_kCdN[setKey] === undefined) {
9682
9722
  makeInfoWindow(g_msgInfoObj.I_0002, `fadeOut0`);
@@ -9685,14 +9725,20 @@ const keyConfigInit = (_kcType = g_kcType) => {
9685
9725
  return;
9686
9726
  }
9687
9727
  if (selectedKc === `TitleBack` || selectedKc === `Retry`) {
9728
+ // タイトルバックキー、リトライキーはプレイ中の操作キーと重複しないようにする(簡易的)
9729
+ if (g_keyObj[`keyCtrl${keyCtrlPtn}`].flat().includes(setKey)) {
9730
+ makeInfoWindow(g_msgInfoObj.I_0002, `fadeOut0`);
9731
+ return;
9732
+ }
9688
9733
  // プレイ中ショートカットキー変更
9689
9734
  g_headerObj[`key${selectedKc}`] = setKey;
9690
9735
  g_headerObj[`key${selectedKc}Def`] = setKey;
9691
9736
  document.getElementById(`sc${selectedKc}`).textContent = getScMsg[selectedKc]();
9692
- document.getElementById(`sc${selectedKc}`).style.fontSize = `${getFontSize2(getScMsg[selectedKc](), g_btnWidth(5 / 12) - 40, { maxSiz: 13 })}px`;
9737
+ document.getElementById(`sc${selectedKc}`).style.fontSize =
9738
+ `${getFontSize2(getScMsg[selectedKc](), g_btnWidth(5 / 12) - 40, { maxSiz: g_limitObj.mainSiz })}px`;
9693
9739
  if (g_isMac) {
9694
9740
  scTitleBack.textContent = getScMsg.TitleBack();
9695
- scTitleBack.style.fontSize = `${getFontSize2(getScMsg.TitleBack(), g_btnWidth(5 / 12) - 40, { maxSiz: 13 })}px`;
9741
+ scTitleBack.style.fontSize = `${getFontSize2(getScMsg.TitleBack(), g_btnWidth(5 / 12) - 40, { maxSiz: g_limitObj.mainSiz })}px`;
9696
9742
  }
9697
9743
  changeConfigColor(document.getElementById(`sc${selectedKc}`),
9698
9744
  g_headerObj[`key${selectedKc}`] === g_headerObj[`key${selectedKc}Def2`] ?
@@ -9700,7 +9746,12 @@ const keyConfigInit = (_kcType = g_kcType) => {
9700
9746
  return;
9701
9747
  }
9702
9748
 
9703
- if (setKey === C_KEY_TITLEBACK && g_currentk === 0) {
9749
+ if (setKey === C_KEY_ESCAPE) {
9750
+ // リトライキー、タイトルバックキーにEscキーを割り当て可能にするため、
9751
+ // 例外的にEscキーで戻る対応をここで処理
9752
+ btnBack.click();
9753
+ return;
9754
+ } else if (setKey === C_KEY_TITLEBACK && g_currentk === 0) {
9704
9755
  return;
9705
9756
  }
9706
9757
 
@@ -9732,7 +9783,14 @@ const keyConfigInit = (_kcType = g_kcType) => {
9732
9783
  }
9733
9784
  });
9734
9785
 
9735
- g_skinJsObj.keyconfig.forEach(func => func());
9786
+ // 戻るボタンのショートカットキー表示
9787
+ multiAppend(btnBack,
9788
+ createDivCss2Label(`scKeyConfigBack`, `${g_lblNameObj.sc_keyConfigBack})`, {
9789
+ x: 0, siz: 12, fontWeight: `bold`, opacity: 0.75, align: C_ALIGN_LEFT,
9790
+ })
9791
+ );
9792
+
9793
+ safeExecuteCustomHooks(`g_skinJsObj.keyconfig`, g_skinJsObj.keyconfig);
9736
9794
  document.onkeyup = evt => commonKeyUp(evt);
9737
9795
  document.oncontextmenu = () => false;
9738
9796
  };
@@ -9922,7 +9980,7 @@ const loadMusic = () => {
9922
9980
  lblLoading.textContent = `${g_lblNameObj.nowLoading} ${_event.loaded}Bytes`;
9923
9981
  }
9924
9982
  // ユーザカスタムイベント
9925
- g_customJsObj.progress.forEach(func => func(_event));
9983
+ safeExecuteCustomHooks(`g_customJsObj.progress`, g_customJsObj.progress, _event);
9926
9984
  });
9927
9985
 
9928
9986
  // エラー処理
@@ -10030,8 +10088,8 @@ const loadingScoreInit = async () => {
10030
10088
  g_headerObj.blankFrame = g_headerObj.blankFrameDef;
10031
10089
 
10032
10090
  // ユーザカスタムイベント
10033
- g_customJsObj.preloading.forEach(func => func());
10034
- g_skinJsObj.preloading.forEach(func => func());
10091
+ safeExecuteCustomHooks(`g_customJsObj.preloading`, g_customJsObj.preloading);
10092
+ safeExecuteCustomHooks(`g_skinJsObj.preloading`, g_skinJsObj.preloading);
10035
10093
 
10036
10094
  let dummyIdHeader = ``;
10037
10095
  if (g_stateObj.dummyId !== ``) {
@@ -10170,7 +10228,7 @@ const loadingScoreInit = async () => {
10170
10228
  getArrowSettings();
10171
10229
 
10172
10230
  // ユーザカスタムイベント
10173
- g_customJsObj.loading.forEach(func => func());
10231
+ safeExecuteCustomHooks(`g_customJsObj.loading`, g_customJsObj.loading);
10174
10232
 
10175
10233
  mainInit();
10176
10234
  };
@@ -11360,7 +11418,7 @@ const pushArrows = (_dataObj, _speedOnFrame, _firstArrivalFrame) => {
11360
11418
  * - この関数を使用する場合、配列グループの先頭2つが「フレーム数、矢印番号」となっていないと動作しない
11361
11419
  * @param {string} _type
11362
11420
  * @param {string} _header
11363
- * @param {function} _setFunc 後続実行関数
11421
+ * @param {Function} _setFunc 後続実行関数
11364
11422
  * @param {number} object._term 1セット当たりのデータ数(デフォルトは後続実行関数の引数の数-1, デフォルト引数・オブジェクト引数除く)
11365
11423
  * @param {boolean} object._colorFlg 個別色変化フラグ
11366
11424
  * @param {boolean} object._calcFrameFlg 逆算を無条件で行うかどうかの可否
@@ -12669,7 +12727,7 @@ const mainInit = () => {
12669
12727
  }
12670
12728
 
12671
12729
  // ユーザカスタムイベント(初期)
12672
- g_customJsObj.main.forEach(func => func());
12730
+ safeExecuteCustomHooks(`g_customJsObj.main`, g_customJsObj.main);
12673
12731
 
12674
12732
  /**
12675
12733
  * キーを押したときの処理
@@ -12917,7 +12975,7 @@ const mainInit = () => {
12917
12975
  if (_cnt === 0) {
12918
12976
  const stepDivHit = document.getElementById(`stepHit${_j}`);
12919
12977
 
12920
- g_customJsObj.dummyArrow.forEach(func => func());
12978
+ safeExecuteCustomHooks(`g_customJsObj.dummyArrow`, g_customJsObj.dummyArrow);
12921
12979
  stepDivHit.style.top = wUnit(-15);
12922
12980
  stepDivHit.style.opacity = 1;
12923
12981
  stepDivHit.classList.value = ``;
@@ -12937,7 +12995,7 @@ const mainInit = () => {
12937
12995
 
12938
12996
  // ダミーフリーズアロー(成功時)
12939
12997
  dummyFrzOK: (_j, _k, _frzName, _cnt) => {
12940
- g_customJsObj.dummyFrz.forEach(func => func());
12998
+ safeExecuteCustomHooks(`g_customJsObj.dummyFrz`, g_customJsObj.dummyFrz);
12941
12999
  $id(`frzHit${_j}`).opacity = 0;
12942
13000
  g_attrObj[_frzName].judgEndFlg = true;
12943
13001
  judgeObjDelete.dummyFrz(_j, _frzName);
@@ -13157,7 +13215,7 @@ const mainInit = () => {
13157
13215
  arrowSubRoot.appendChild(createColorObject2(`${_name}Top${_j}_${_arrowCnt}`, {
13158
13216
  background: _color, rotate: g_workObj.arrowRtn[_j],
13159
13217
  }));
13160
- g_customJsObj.makeArrow.forEach(func => func(_attrs, arrowName, _name, _arrowCnt));
13218
+ safeExecuteCustomHooks(`g_customJsObj.makeArrow`, g_customJsObj.makeArrow, _attrs, arrowName, _name, _arrowCnt);
13161
13219
  };
13162
13220
 
13163
13221
  /**
@@ -13326,7 +13384,7 @@ const mainInit = () => {
13326
13384
  addTransform(frzName, `rootX`, `translateX(${g_workObj.stepX[_j]}px)`);
13327
13385
  }
13328
13386
 
13329
- g_customJsObj.makeFrzArrow.forEach(func => func(_attrs, frzName, _name, _arrowCnt));
13387
+ safeExecuteCustomHooks(`g_customJsObj.makeFrzArrow`, g_customJsObj.makeFrzArrow, _attrs, frzName, _name, _arrowCnt);
13330
13388
  };
13331
13389
 
13332
13390
  /**
@@ -13445,7 +13503,7 @@ const mainInit = () => {
13445
13503
  }
13446
13504
 
13447
13505
  // ユーザカスタムイベント(フレーム毎)
13448
- g_customJsObj.mainEnterFrame.forEach(func => func());
13506
+ safeExecuteCustomHooks(`g_customJsObj.mainEnterFrame`, g_customJsObj.mainEnterFrame);
13449
13507
 
13450
13508
  // 速度変化 (途中変速, 個別加速)
13451
13509
  while (currentFrame >= g_workObj.speedData?.[speedCnts]) {
@@ -13643,7 +13701,7 @@ const mainInit = () => {
13643
13701
  g_timeoutEvtId = setTimeout(flowTimeline, 1000 / g_fps - buffTime);
13644
13702
  }
13645
13703
  };
13646
- g_skinJsObj.main.forEach(func => func());
13704
+ safeExecuteCustomHooks(`g_skinJsObj.main`, g_skinJsObj.main);
13647
13705
 
13648
13706
  g_audio.currentTime = firstFrame / g_fps * g_headerObj.playbackRate;
13649
13707
  g_audio.playbackRate = g_headerObj.playbackRate;
@@ -13863,7 +13921,7 @@ const changeAppearanceFilter = (_num = 10) => {
13863
13921
  }
13864
13922
 
13865
13923
  // ユーザカスタムイベント(アルファマスクの再描画)
13866
- g_customJsObj.appearanceFilter.forEach(func => func(topNum, bottomNum));
13924
+ safeExecuteCustomHooks(`g_customJsObj.appearanceFilter`, g_customJsObj.appearanceFilter, topNum, bottomNum);
13867
13925
 
13868
13926
  return doubleFilterFlg;
13869
13927
  };
@@ -14187,7 +14245,7 @@ const changeHitFrz = (_j, _k, _name, _difFrame = 0) => {
14187
14245
  if (g_stateObj.frzReturn !== C_FLG_OFF) {
14188
14246
  startFrzReturn();
14189
14247
  }
14190
- g_customJsObj[`judg_${_name}Hit`].forEach(func => func(_difFrame));
14248
+ safeExecuteCustomHooks(`g_customJsObj.judg_${_name}Hit`, g_customJsObj[`judg_${_name}Hit`], _difFrame);
14191
14249
  };
14192
14250
 
14193
14251
  /**
@@ -14444,7 +14502,7 @@ const judgeRecovery = (_name, _difFrame) => {
14444
14502
  if (_name === `shakin`) {
14445
14503
  quickRetry(`Shakin(Great)`);
14446
14504
  }
14447
- g_customJsObj[`judg_${_name}`].forEach(func => func(_difFrame));
14505
+ safeExecuteCustomHooks(`g_customJsObj.judg_${_name}`, g_customJsObj[`judg_${_name}`], _difFrame);
14448
14506
  };
14449
14507
 
14450
14508
  /**
@@ -14458,7 +14516,7 @@ const judgeDamage = (_name, _difFrame) => {
14458
14516
  comboJ.textContent = ``;
14459
14517
  diffJ.textContent = ``;
14460
14518
  lifeDamage();
14461
- g_customJsObj[`judg_${_name}`].forEach(func => func(_difFrame));
14519
+ safeExecuteCustomHooks(`g_customJsObj.judg_${_name}`, g_customJsObj[`judg_${_name}`], _difFrame);
14462
14520
  };
14463
14521
 
14464
14522
  /**
@@ -14483,7 +14541,7 @@ const judgeMatari = _difFrame => {
14483
14541
  finishViewing();
14484
14542
  quickRetry(`Matari(Good)`);
14485
14543
 
14486
- g_customJsObj.judg_matari.forEach(func => func(_difFrame));
14544
+ safeExecuteCustomHooks(`g_customJsObj.judg_matari`, g_customJsObj.judg_matari, _difFrame);
14487
14545
  };
14488
14546
 
14489
14547
  /**
@@ -14514,7 +14572,7 @@ const judgeKita = _difFrame => {
14514
14572
  lifeRecovery();
14515
14573
  finishViewing();
14516
14574
 
14517
- g_customJsObj.judg_kita.forEach(func => func(_difFrame));
14575
+ safeExecuteCustomHooks(`g_customJsObj.judg_kita`, g_customJsObj.judg_kita, _difFrame);
14518
14576
  };
14519
14577
 
14520
14578
  /**
@@ -14528,7 +14586,7 @@ const judgeIknai = _difFrame => {
14528
14586
 
14529
14587
  lifeDamage();
14530
14588
 
14531
- g_customJsObj.judg_iknai.forEach(func => func(_difFrame));
14589
+ safeExecuteCustomHooks(`g_customJsObj.judg_iknai`, g_customJsObj.judg_iknai, _difFrame);
14532
14590
  };
14533
14591
 
14534
14592
  const jdgList = [`ii`, `shakin`, `matari`, `shobon`].map(jdg => toCapitalize(jdg));
@@ -14990,7 +15048,7 @@ const resultInit = () => {
14990
15048
 
14991
15049
  // ユーザカスタムイベント(初期)
14992
15050
  const currentDateTime = new Date().toLocaleString();
14993
- g_customJsObj.result.forEach(func => func());
15051
+ safeExecuteCustomHooks(`g_customJsObj.result`, g_customJsObj.result);
14994
15052
 
14995
15053
  if (highscorePreCondition) {
14996
15054
 
@@ -15263,7 +15321,7 @@ const resultInit = () => {
15263
15321
  * @param {string} _id
15264
15322
  * @param {string} _name
15265
15323
  * @param {object} _posObj
15266
- * @param {function} _func
15324
+ * @param {Function} _func
15267
15325
  * @param {...any} _cssClass
15268
15326
  * @returns {HTMLDivElement}
15269
15327
  */
@@ -15334,7 +15392,7 @@ const resultInit = () => {
15334
15392
  const flowResultTimeline = () => {
15335
15393
 
15336
15394
  // ユーザカスタムイベント(フレーム毎)
15337
- g_customJsObj.resultEnterFrame.forEach(func => func());
15395
+ safeExecuteCustomHooks(`g_customJsObj.resultEnterFrame`, g_customJsObj.resultEnterFrame);
15338
15396
 
15339
15397
  // 背景・マスクモーション、スキン変更
15340
15398
  drawTitleResultMotion(g_currentPage);
@@ -15368,7 +15426,7 @@ const resultInit = () => {
15368
15426
  setShortcutEvent(g_currentPage, () => true, { dfEvtFlg: true });
15369
15427
  document.oncontextmenu = () => true;
15370
15428
 
15371
- g_skinJsObj.result.forEach(func => func());
15429
+ safeExecuteCustomHooks(`g_skinJsObj.result`, g_skinJsObj.result);
15372
15430
  };
15373
15431
 
15374
15432
  /**
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * Source by tickle
7
7
  * Created : 2019/11/19
8
- * Revised : 2026/03/26 (v46.4.0)
8
+ * Revised : 2026/03/30 (v46.5.1)
9
9
  *
10
10
  * https://github.com/cwtickle/danoniplus
11
11
  */
@@ -963,6 +963,7 @@ const g_escapeStr = {
963
963
  const g_emojiObj = {
964
964
  checkMark: `✔`, // チェックマーク (check mark)
965
965
  crossMark: `❌`, // バツ (cross mark)
966
+ policeLight: `🚨`, // パトランプ (police car light)
966
967
  muted: `🔇`, // 無音のスピーカー (muted speaker)
967
968
  speaker: `🔊`, // 音量大のスピーカー (speaker high volume)
968
969
  shield: `🛡`, // 盾 (shield)
@@ -2902,7 +2903,7 @@ const g_shortcutObj = {
2902
2903
  KeyU: { id: `btnSettingSummary` },
2903
2904
  },
2904
2905
  keyConfig: {
2905
- Escape: { id: `btnBack` },
2906
+ //Escape: { id: `btnBack` }, // Escapeを別用途でも使用するため、ここではコメントアウト
2906
2907
  Backspace_Enter: { id: `btnPlay` },
2907
2908
  Backspace_NumpadEnter: { id: `btnPlay` },
2908
2909
  },
@@ -4398,6 +4399,7 @@ const g_lblNameObj = {
4398
4399
  sc_scroll: `R/<br>↑↓`,
4399
4400
  sc_adjustment: `- +`,
4400
4401
  sc_hitPosition: `T B`,
4402
+ sc_keyConfigBack: `Esc`,
4401
4403
  sc_keyConfigPlay: g_isMac ? `Del+Enter` : `BS+Enter`,
4402
4404
 
4403
4405
  g_start: `Start`,
@@ -4827,6 +4829,8 @@ const g_lang_msgObj = {
4827
4829
  pickColorR: `設定する矢印色の種類を切り替えます。`,
4828
4830
  pickColorCopy: `このボタンを押すと、フリーズアローの配色を矢印(枠)の色で上書きします。\nヒット時のフリーズアローの色も上書きします。`,
4829
4831
  pickColorReset: `矢印・フリーズアローの配色を元に戻します。`,
4832
+
4833
+ customFunctionError: `初回の実行エラーが発生しました。修正されるまでこの処理は無視されます。`,
4830
4834
  },
4831
4835
 
4832
4836
  En: {
@@ -4925,6 +4929,8 @@ const g_lang_msgObj = {
4925
4929
  pickColorR: `Switches the arrow color type to be set.`,
4926
4930
  pickColorCopy: `Pressing this button will override the color scheme of the freeze arrow with the frame color of the arrow. \nIt also overrides the color of the freeze arrow on hit.`,
4927
4931
  pickColorReset: `Restore the color scheme for arrows and freeze arrows.`,
4932
+
4933
+ customFunctionError: `An error occurred during the first execution. This process will be ignored until the error is resolved.`,
4928
4934
  },
4929
4935
 
4930
4936
  };
@@ -5009,6 +5015,12 @@ const g_skinJsObj = {
5009
5015
  result: [],
5010
5016
  };
5011
5017
 
5018
+ const g_errorCache = {
5019
+ 'g_customJsObj.titleEnterFrame': [],
5020
+ 'g_customJsObj.mainEnterFrame': [],
5021
+ 'g_customJsObj.resultEnterFrame': [],
5022
+ };
5023
+
5012
5024
  /**
5013
5025
  * 従来のカスタム関数をg_customJsObj, g_skinJsObjへ追加
5014
5026
  * - customjsファイルを読み込んだ直後にこの関数を呼び出している
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "danoniplus",
3
- "version": "46.4.0",
3
+ "version": "46.5.1",
4
4
  "description": "Dancing☆Onigiri (CW Edition) - Web-based Rhythm Game",
5
5
  "main": "./js/danoni_main.js",
6
6
  "jsdelivr": "./js/danoni_main.js",