danoniplus 39.4.1 → 39.5.0

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/js/danoni_main.js CHANGED
@@ -4,19 +4,19 @@
4
4
  *
5
5
  * Source by tickle
6
6
  * Created : 2018/10/08
7
- * Revised : 2025/02/11
7
+ * Revised : 2025/02/15
8
8
  *
9
9
  * https://github.com/cwtickle/danoniplus
10
10
  */
11
- const g_version = `Ver 39.4.1`;
12
- const g_revisedDate = `2025/02/11`;
11
+ const g_version = `Ver 39.5.0`;
12
+ const g_revisedDate = `2025/02/15`;
13
13
 
14
14
  // カスタム用バージョン (danoni_custom.js 等で指定可)
15
15
  let g_localVersion = ``;
16
16
  let g_localVersion2 = ``;
17
17
 
18
18
  // ショートカット用文字列(↓の文字列を検索することで対象箇所へジャンプできます)
19
- // 共通:water 初期化:peach タイトル:melon 設定:lime ディスプレイ:lemon 拡張設定:apple キーコンフィグ:orange 譜面読込:strawberry メイン:banana 結果:grape
19
+ // 共通:water 初期化:peach タイトル:melon データ管理:pear 設定:lime ディスプレイ:lemon 拡張設定:apple キーコンフィグ:orange 譜面読込:strawberry メイン:banana 結果:grape
20
20
  // シーンジャンプ:Scene
21
21
 
22
22
  /**
@@ -406,6 +406,24 @@ const splitLF2 = (_str, _delim = `$`) => splitLF(_str)?.filter(val => val !== ``
406
406
  */
407
407
  const splitComma = _str => _str?.split(`, `).join(`*comma* `).split(`,`);
408
408
 
409
+ /**
410
+ * ストレージ処理のパース
411
+ * @param {string} _keyName
412
+ * @param {Object} _default
413
+ * @returns {Object}
414
+ */
415
+ const parseStorageData = (_keyName, _default = {}) => {
416
+ const storageText = localStorage.getItem(_keyName);
417
+ if (storageText === null) {
418
+ return _default;
419
+ }
420
+ try {
421
+ return JSON.parse(storageText);
422
+ } catch (err) {
423
+ return _default;
424
+ }
425
+ }
426
+
409
427
  /**
410
428
  * 重複を排除した配列の生成
411
429
  * @param {any[]} _array1
@@ -1166,7 +1184,7 @@ const setUserSelect = (_style, _value = C_DIS_NONE) => {
1166
1184
  * @returns {HTMLDivElement}
1167
1185
  */
1168
1186
  const createDivCss2Label = (_id, _text, { x = 0, y = 0, w = g_limitObj.setLblWidth, h = g_limitObj.setLblHeight,
1169
- siz = g_limitObj.setLblSiz, align = C_ALIGN_CENTER, ...rest } = {}, ..._classes) => {
1187
+ siz = g_limitObj.setLblSiz, align = C_ALIGN_CENTER, type = `text`, ...rest } = {}, ..._classes) => {
1170
1188
  const div = createDiv(_id, x, y, w, h, [g_cssObj.title_base, ..._classes]);
1171
1189
 
1172
1190
  const style = div.style;
@@ -1174,6 +1192,9 @@ const createDivCss2Label = (_id, _text, { x = 0, y = 0, w = g_limitObj.setLblWid
1174
1192
  style.fontFamily = getBasicFont();
1175
1193
  style.textAlign = `${align}`;
1176
1194
  style.pointerEvents = C_DIS_NONE;
1195
+ if (rest?.overflow === C_DIS_AUTO || type !== `text`) {
1196
+ style.pointerEvents = C_DIS_AUTO;
1197
+ }
1177
1198
  div.innerHTML = _text;
1178
1199
  Object.keys(rest).forEach(property => style[property] = rest[property]);
1179
1200
 
@@ -1255,6 +1276,9 @@ const createColorObject2 = (_id,
1255
1276
  style.webkitMaskImage = `url("${g_imgObj[charaStyle]}")`;
1256
1277
  style.webkitMaskSize = `contain`;
1257
1278
  style.pointerEvents = C_DIS_NONE;
1279
+ if (rest?.overflow === C_DIS_AUTO) {
1280
+ style.pointerEvents = C_DIS_AUTO;
1281
+ }
1258
1282
  Object.keys(rest).forEach(property => style[property] = rest[property]);
1259
1283
  setAttrs(div, { color: rest.background ?? ``, type: charaStyle, cnt: 0, });
1260
1284
 
@@ -1284,7 +1308,8 @@ const createEmptySprite = (_parentObj, _newObjId, { x = 0, y = 0, w = g_sWidth,
1284
1308
  div.title = title;
1285
1309
 
1286
1310
  const style = div.style;
1287
- style.pointerEvents = title === `` ? C_DIS_NONE : C_DIS_AUTO;
1311
+ style.pointerEvents = (title !== `` || rest?.overflow === C_DIS_AUTO)
1312
+ ? C_DIS_AUTO : C_DIS_NONE;
1288
1313
  Object.keys(rest).forEach(property => style[property] = rest[property]);
1289
1314
  _parentObj.appendChild(div);
1290
1315
 
@@ -2344,9 +2369,8 @@ const loadLocalStorage = () => {
2344
2369
  };
2345
2370
 
2346
2371
  // ロケールの読込、警告メッセージの入替
2347
- const checkLocale = localStorage.getItem(`danoni-locale`);
2348
- if (checkLocale) {
2349
- g_langStorage = JSON.parse(checkLocale);
2372
+ g_langStorage = parseStorageData(`danoni-locale`);
2373
+ if (g_langStorage.locale !== undefined) {
2350
2374
  g_localeObj.val = g_langStorage.locale;
2351
2375
  g_localeObj.num = g_localeObj.list.findIndex(val => val === g_localeObj.val);
2352
2376
  }
@@ -2354,34 +2378,25 @@ const loadLocalStorage = () => {
2354
2378
  Object.assign(g_kCd, g_lang_kCd[g_localeObj.val]);
2355
2379
 
2356
2380
  // 作品別ローカルストレージの読込
2357
- const checkStorage = localStorage.getItem(g_localStorageUrl);
2358
- if (checkStorage) {
2359
- g_localStorage = JSON.parse(checkStorage);
2360
-
2361
- // Adjustment, Volume, Appearance, Opacity, HitPosition初期値設定
2362
- checkLocalParam(`adjustment`, C_TYP_FLOAT, g_settings.adjustmentNum);
2363
- checkLocalParam(`volume`, C_TYP_NUMBER, g_settings.volumes.length - 1);
2364
- checkLocalParam(`appearance`);
2365
- checkLocalParam(`opacity`, C_TYP_NUMBER, g_settings.opacitys.length - 1);
2366
- checkLocalParam(`hitPosition`, C_TYP_FLOAT, g_settings.hitPositionNum);
2367
-
2368
- // ハイスコア取得準備
2369
- if (g_localStorage.highscores === undefined) {
2370
- g_localStorage.highscores = {};
2371
- }
2381
+ g_localStorage = parseStorageData(g_localStorageUrl, {
2382
+ adjustment: 0, hitPosition: 0, volume: 100, highscores: {},
2383
+ });
2372
2384
 
2373
- // 廃棄済みリストからデータを消去
2374
- g_storeSettingsEx.filter(val => g_localStorage[val] !== undefined)
2375
- .forEach(val => delete g_localStorage[val]);
2385
+ // Adjustment, Volume, Appearance, Opacity, HitPosition初期値設定
2386
+ checkLocalParam(`adjustment`, C_TYP_FLOAT, g_settings.adjustmentNum);
2387
+ checkLocalParam(`volume`, C_TYP_NUMBER, g_settings.volumes.length - 1);
2388
+ checkLocalParam(`appearance`);
2389
+ checkLocalParam(`opacity`, C_TYP_NUMBER, g_settings.opacitys.length - 1);
2390
+ checkLocalParam(`hitPosition`, C_TYP_FLOAT, g_settings.hitPositionNum);
2376
2391
 
2377
- } else {
2378
- g_localStorage = {
2379
- adjustment: 0,
2380
- hitPosition: 0,
2381
- volume: 100,
2382
- highscores: {},
2383
- };
2392
+ // ハイスコア取得準備
2393
+ if (g_localStorage.highscores === undefined) {
2394
+ g_localStorage.highscores = {};
2384
2395
  }
2396
+
2397
+ // 廃棄済みリストからデータを消去
2398
+ g_storeSettingsEx.filter(val => g_localStorage[val] !== undefined)
2399
+ .forEach(val => delete g_localStorage[val]);
2385
2400
  };
2386
2401
 
2387
2402
  /**
@@ -4470,8 +4485,6 @@ const titleInit = () => {
4470
4485
  }
4471
4486
  const releaseDate = (g_headerObj.releaseDate !== `` ? ` @${g_headerObj.releaseDate}` : ``);
4472
4487
  const versionName = `© 2018-${g_revisedDate.slice(0, 4)} ティックル, CW ${g_version}${customVersion}${releaseDate}`;
4473
-
4474
- let reloadFlg = false;
4475
4488
  const getLinkSiz = _name => getFontSize(_name, g_sWidth / 2 - 20, getBasicFont(), g_limitObj.lnkSiz, 12);
4476
4489
 
4477
4490
  /**
@@ -4495,23 +4508,9 @@ const titleInit = () => {
4495
4508
 
4496
4509
  // Reset
4497
4510
  createCss2Button(`btnReset`, g_lblNameObj.dataReset, () => {
4498
- reloadFlg = false;
4499
- if (window.confirm(g_msgObj.dataResetConfirm)) {
4500
- g_localStorage = {
4501
- adjustment: 0,
4502
- volume: 100,
4503
- highscores: {},
4504
- };
4505
- localStorage.setItem(g_localStorageUrl, JSON.stringify(g_localStorage));
4506
- reloadFlg = true;
4507
- }
4508
- }, Object.assign(g_lblPosObj.btnReset, {
4509
- resetFunc: () => {
4510
- if (reloadFlg) {
4511
- location.reload();
4512
- }
4513
- },
4514
- }), g_cssObj.button_Reset),
4511
+ clearTimeout(g_timeoutEvtTitleId);
4512
+ dataMgtInit();
4513
+ }, g_lblPosObj.btnReset, g_cssObj.button_Reset),
4515
4514
 
4516
4515
  // ロケール切替
4517
4516
  createCss2Button(`btnReload`, g_localeObj.val, () => true,
@@ -4693,6 +4692,249 @@ const setWindowStyle = (_text, _bkColor, _textColor, _align = C_ALIGN_LEFT, { _x
4693
4692
  return lbl;
4694
4693
  };
4695
4694
 
4695
+ /*-----------------------------------------------------------*/
4696
+ /* Scene : DATA MANAGEMENT [pear] */
4697
+ /*-----------------------------------------------------------*/
4698
+
4699
+ const dataMgtInit = () => {
4700
+ clearWindow(true);
4701
+ g_currentPage = `dataMgt`;
4702
+
4703
+ multiAppend(divRoot,
4704
+
4705
+ // 画面タイトル
4706
+ getTitleDivLabel(`lblTitle`,
4707
+ `<div class="settings_Title">DATA</div><div class="settings_Title2">MANAGEMENT</div>`
4708
+ .replace(/[\t\n]/g, ``), 0, 15, g_cssObj.flex_centering),
4709
+
4710
+ createDescDiv(`dataDelMsg`, g_lblNameObj.dataDeleteDesc),
4711
+ );
4712
+
4713
+ // 各ボタン用のスプライトを作成
4714
+ const optionsprite = createEmptySprite(divRoot, `optionsprite`, g_windowObj.optionSprite);
4715
+
4716
+ let reloadFlg = false;
4717
+ const list = [C_FLG_OFF, C_FLG_ON];
4718
+ const cssBarList = [C_FLG_OFF, C_FLG_ON];
4719
+ const cssBgList = [g_settings.d_cssBgName, g_settings.d_cssBgName];
4720
+
4721
+ /**
4722
+ * データ管理用ラベルの作成
4723
+ * @param {string} _name
4724
+ * @param {number} _heightPos
4725
+ * @param {number} [x=0]
4726
+ * @returns {HTMLDivElement}
4727
+ */
4728
+ const createMgtLabel = (_name, _heightPos, { x = 0 } = {}) =>
4729
+ createDivCss2Label(`lbl${toCapitalize(_name)}`, getStgDetailName(toCapitalize(_name)), {
4730
+ x, y: g_limitObj.setLblHeight * _heightPos + 40,
4731
+ siz: g_limitObj.setLblSiz, align: C_ALIGN_LEFT,
4732
+ });
4733
+
4734
+ /**
4735
+ * データ管理用ボタンの作成
4736
+ * @param {string} _name
4737
+ * @param {number} _heightPos
4738
+ * @param {number} _widthPos
4739
+ * @param {number} [w=125]
4740
+ * @param {function} func
4741
+ * @returns {HTMLDivElement}
4742
+ */
4743
+ const createMgtButton = (_name, _heightPos, _widthPos, { w = 125, func = () => true, ...rest } = {}) => {
4744
+ const linkId = `btn${toCapitalize(_name)}`;
4745
+ return createCss2Button(linkId, getStgDetailName(toCapitalize(_name)), () => {
4746
+ const prevDisp = g_settings.dataMgtNum[_name];
4747
+ const [prevBarColor, prevBgColor] = [cssBarList[prevDisp], cssBgList[prevDisp]];
4748
+
4749
+ g_settings.dataMgtNum[_name] = (g_settings.dataMgtNum[_name] + 1) % 2;
4750
+ g_stateObj[`dm_${_name}`] = list[g_settings.dataMgtNum[_name]];
4751
+
4752
+ const nextDisp = g_settings.dataMgtNum[_name];
4753
+ const [nextBarColor, nextBgColor] = [cssBarList[nextDisp], cssBgList[nextDisp]];
4754
+ document.getElementById(linkId).classList.replace(g_cssObj[`button_${prevBarColor}`], g_cssObj[`button_${nextBarColor}`]);
4755
+ document.getElementById(linkId).classList.replace(g_cssObj[`button_${prevBgColor}`], g_cssObj[`button_${nextBgColor}`]);
4756
+ func();
4757
+ }, {
4758
+ x: _widthPos * (w + 5) + 20, y: g_limitObj.setLblHeight * _heightPos + 40,
4759
+ w, h: 20, siz: g_limitObj.setLblSiz, borderStyle: `solid`, title: g_msgObj[_name], ...rest
4760
+ }, g_cssObj[`button_${cssBgList[g_settings.dataMgtNum[_name]]}`], g_cssObj[`button_${cssBarList[g_settings.dataMgtNum[_name]]}`]);
4761
+ };
4762
+
4763
+ /**
4764
+ * キー別ストレージ情報の取得
4765
+ * @param {string} _key
4766
+ * @returns {string}
4767
+ */
4768
+ const viewKeyStorage = _key => {
4769
+
4770
+ // キャッシュ設定
4771
+ if (!viewKeyStorage.cache) {
4772
+ viewKeyStorage.cache = new Map();
4773
+ }
4774
+ if (viewKeyStorage.cache.has(_key)) {
4775
+ return viewKeyStorage.cache.get(_key);
4776
+ }
4777
+
4778
+ let keyStorage = parseStorageData(`danonicw-${_key}k`);
4779
+ if (Object.keys(keyStorage).length === 0) {
4780
+
4781
+ // キー別の情報が見つからない場合は作品別の情報から検索
4782
+ Object.keys(g_localStorage).filter(val => val.endsWith(_key))
4783
+ .forEach(val => keyStorage[val] = g_localStorage[val]);
4784
+ if (Object.keys(keyStorage).length === 0) {
4785
+ return ``;
4786
+ }
4787
+ }
4788
+ const result = formatObject(keyStorage);
4789
+ viewKeyStorage.cache.set(_key, result);
4790
+ return result;
4791
+ }
4792
+
4793
+ /**
4794
+ * 画面表示用インデント処理
4795
+ * @param {number} _level
4796
+ * @returns {string}
4797
+ */
4798
+ const getIndent = (_level) => '&nbsp;'.repeat(_level * 4);
4799
+
4800
+ /**
4801
+ * オブジェクトのネスト表示処理
4802
+ * @param {Object} _obj
4803
+ * @param {Number} _indent
4804
+ * @returns {string}
4805
+ */
4806
+ const formatObject = (_obj, _indent = 0) => {
4807
+ const baseIndent = getIndent(_indent);
4808
+ const nestedIndent = getIndent(_indent + 1);
4809
+ const formattedEntries = Object.entries(_obj)
4810
+ .map(([key, value]) => {
4811
+ const isNestedObject = typeof value === 'object' && value !== null && !Array.isArray(value);
4812
+ const formattedValue = isNestedObject ? formatObject(value, _indent + 1) : JSON.stringify(value);
4813
+ return `<br>${nestedIndent}"${key}": ${formattedValue}`;
4814
+ }).join(`,`);
4815
+ return `{${formattedEntries}<br>${baseIndent}}`;
4816
+ }
4817
+
4818
+ multiAppend(optionsprite,
4819
+ createMgtLabel(`workData`, 0),
4820
+ createMgtButton(`environment`, 1.5, 0),
4821
+ createMgtButton(`highscores`, 2.5, 0),
4822
+ createMgtButton(`customKey`, 3.5, 0),
4823
+ createMgtButton(`others`, 4.5, 0),
4824
+ createMgtLabel(`keyData`, 6),
4825
+ createDivCss2Label(`lblTargetKey`, `(${getKeyName(g_headerObj.keyLabels[0])})`, {
4826
+ x: 90, y: g_limitObj.setLblHeight * 6 + 40,
4827
+ siz: g_limitObj.setLblSiz, align: C_ALIGN_LEFT,
4828
+ })
4829
+ );
4830
+
4831
+ // カスタムキー定義のストレージデータを表示から除去
4832
+ const settingStorage = {};
4833
+ Object.keys(g_localStorage).filter(val => !listMatching(val, g_settings.keyStorages.concat(`setColor`), { prefix: `^` }))
4834
+ .forEach(val => settingStorage[val] = g_localStorage[val]);
4835
+
4836
+ multiAppend(divRoot,
4837
+ createDivCss2Label(`lblWorkDataView`,
4838
+ formatObject(settingStorage), g_lblPosObj.lblWorkDataView),
4839
+ createDivCss2Label(`lblKeyDataView`, viewKeyStorage(g_headerObj.keyLabels[0]), g_lblPosObj.lblKeyDataView),
4840
+ );
4841
+ setUserSelect($id(`lblWorkDataView`), `text`);
4842
+ setUserSelect($id(`lblKeyDataView`), `text`);
4843
+
4844
+ const keyList = makeDedupliArray(g_headerObj.keyLabels).sort((a, b) => parseInt(a) - parseInt(b));
4845
+ const keyListSprite = createEmptySprite(optionsprite, `keyListSprite`, g_windowObj.keyListSprite);
4846
+ keyList.forEach((key, j) => {
4847
+ g_stateObj[`dm_${key}`] = C_FLG_OFF;
4848
+ g_settings.dataMgtNum[key] = 0;
4849
+ keyListSprite.appendChild(createMgtButton(key, j - 2, 0, {
4850
+ w: Math.max(50, getStrWidth(getKeyName(key) + ` `, g_limitObj.setLblSiz, getBasicFont())),
4851
+ func: () => {
4852
+ lblKeyDataView.innerHTML = viewKeyStorage(key);
4853
+ lblTargetKey.innerHTML = `(${getKeyName(key)})`;
4854
+ },
4855
+ }));
4856
+ document.getElementById(`btn${key}`).innerHTML = getKeyName(key);
4857
+ });
4858
+
4859
+ // ユーザカスタムイベント(初期)
4860
+ g_customJsObj.dataMgt.forEach(func => func());
4861
+
4862
+ multiAppend(divRoot,
4863
+ createCss2Button(`btnBack`, g_lblNameObj.b_back, () => true,
4864
+ Object.assign(g_lblPosObj.btnBack, {
4865
+ animationName: (g_initialFlg ? `` : `smallToNormalY`), resetFunc: () => titleInit(),
4866
+ }), g_cssObj.button_Back),
4867
+
4868
+ createCss2Button(`btnReset`, g_lblNameObj.b_cReset, () => {
4869
+ reloadFlg = false;
4870
+ const backupData = new Map();
4871
+
4872
+ const selectedData = Object.keys(g_stateObj)
4873
+ .filter(key => key.startsWith('dm_') && g_stateObj[key] === C_FLG_ON)
4874
+ .map(key => key.slice(`dm_`.length));
4875
+
4876
+ if (window.confirm(g_msgObj.dataResetConfirm +
4877
+ `\n\n${selectedData.map(val => `- ${g_msgObj[val] || g_msgObj.keyTypes.split('{0}').join(val)}`).join(`\n`)}`)) {
4878
+ selectedData.forEach(key => {
4879
+ if (g_resetFunc.has(key)) {
4880
+ backupData.set(key, JSON.parse(JSON.stringify(g_localStorage)));
4881
+ g_resetFunc.get(key)();
4882
+ localStorage.setItem(g_localStorageUrl, JSON.stringify(g_localStorage));
4883
+
4884
+ } else if (keyList.includes(key)) {
4885
+ const storage = parseStorageData(`danonicw-${key}k`);
4886
+
4887
+ if (Object.keys(storage).length > 0) {
4888
+ backupData.set(key, JSON.parse(JSON.stringify(storage)));
4889
+ g_settings.keyStorages.forEach(val => delete storage[val]);
4890
+ localStorage.setItem(`danonicw-${key}k`, JSON.stringify(storage));
4891
+ } else {
4892
+ backupData.set(`XX` + key, JSON.parse(JSON.stringify(g_localStorage)));
4893
+ g_settings.keyStorages.forEach(val => delete g_localStorage[`${val}${key}`]);
4894
+ localStorage.setItem(g_localStorageUrl, JSON.stringify(g_localStorage));
4895
+ }
4896
+ }
4897
+ });
4898
+ reloadFlg = true;
4899
+ sessionStorage.setItem('resetBackup', JSON.stringify(Array.from(backupData.entries())));
4900
+ }
4901
+ }, Object.assign(g_lblPosObj.btnResetN, {
4902
+ resetFunc: () => {
4903
+ if (reloadFlg) {
4904
+ location.reload();
4905
+ }
4906
+ },
4907
+ }), g_cssObj.button_Reset),
4908
+
4909
+ // リカバリー用のボタン
4910
+ createCss2Button(`btnUndo`, g_lblNameObj.b_undo, () => {
4911
+ const backup = JSON.parse(sessionStorage.getItem('resetBackup'));
4912
+ if (backup && window.confirm(g_msgObj.dataRestoreConfirm)) {
4913
+ backup.forEach(([key, data]) => {
4914
+ if (g_resetFunc.has(key) || keyList.includes(key.slice(`XX`.length))) {
4915
+ Object.assign(g_localStorage, data);
4916
+ localStorage.setItem(g_localStorageUrl, JSON.stringify(g_localStorage));
4917
+ } else if (keyList.includes(key)) {
4918
+ localStorage.setItem(`danonicw-${key}k`, JSON.stringify(data));
4919
+ }
4920
+ });
4921
+ sessionStorage.removeItem('resetBackup');
4922
+ location.reload();
4923
+ }
4924
+ }, g_lblPosObj.btnUndo, g_cssObj.button_Tweet)
4925
+ );
4926
+ if (sessionStorage.getItem('resetBackup') === null) {
4927
+ btnUndo.style.display = C_DIS_NONE;
4928
+ }
4929
+
4930
+ // キー操作イベント(デフォルト)
4931
+ setShortcutEvent(g_currentPage, () => true, { dfEvtFlg: true });
4932
+
4933
+ document.oncontextmenu = () => true;
4934
+ divRoot.oncontextmenu = () => false;
4935
+
4936
+ g_skinJsObj.dataMgt.forEach(func => func());
4937
+ };
4696
4938
 
4697
4939
  /*-----------------------------------------------------------*/
4698
4940
  /* Scene : SETTINGS [lime] */
@@ -4737,6 +4979,11 @@ const commonSettingBtn = _labelName => {
4737
4979
  Object.assign(g_lblPosObj.btnSave, {
4738
4980
  cxtFunc: evt => switchSave(evt),
4739
4981
  }), g_cssObj.button_Default, (g_stateObj.dataSaveFlg ? g_cssObj.button_ON : g_cssObj.button_OFF)),
4982
+
4983
+ // データ管理画面へ移動
4984
+ createCss2Button(`btnReset`, g_lblNameObj.dataReset, () => {
4985
+ dataMgtInit();
4986
+ }, g_lblPosObj.btnReset, g_cssObj.button_Reset),
4740
4987
  );
4741
4988
  };
4742
4989
 
@@ -4797,26 +5044,15 @@ const setSpriteList = _settingList => {
4797
5044
  };
4798
5045
 
4799
5046
  /**
4800
- * スライダー共通処理 (Fadein)
4801
- * @param {HTMLInputElement} _slider
4802
- * @param {HTMLDivElement} _link
4803
- * @returns {string}
4804
- */
4805
- const inputSlider = (_slider, _link) => {
4806
- const value = parseInt(_slider.value);
4807
- _link.textContent = `${value}${g_lblNameObj.percent}`;
4808
- return value;
4809
- };
4810
-
4811
- /**
4812
- * スライダー共通処理 (Appearance)
5047
+ * スライダー共通処理 (Fadein, Appearance)
4813
5048
  * @param {HTMLInputElement} _slider
4814
5049
  * @param {HTMLDivElement} _link
5050
+ * @param {string} _type
4815
5051
  * @returns {string}
4816
5052
  */
4817
- const inputSliderAppearance = (_slider, _link) => {
5053
+ const inputSlider = (_slider, _link, _type) => {
4818
5054
  const value = parseInt(_slider.value);
4819
- _link.textContent = `${g_hidSudObj.distH[g_stateObj.appearance](value)}`;
5055
+ _link.textContent = g_sliderView.get(_type)(value);
4820
5056
  return value;
4821
5057
  };
4822
5058
 
@@ -5482,18 +5718,17 @@ const setDifficulty = (_initFlg) => {
5482
5718
  g_keyObj.currentPtn = 0;
5483
5719
  g_keycons.keySwitchNum = 0;
5484
5720
  }
5485
- const hasKeyStorage = localStorage.getItem(`danonicw-${g_keyObj.currentKey}k`);
5486
5721
  let storageObj, addKey = ``;
5487
5722
 
5488
5723
  if (!g_stateObj.extraKeyFlg) {
5489
5724
 
5490
5725
  // キー別のローカルストレージの初期設定 ※特殊キーは除く
5491
- g_localKeyStorage = hasKeyStorage ? JSON.parse(hasKeyStorage) : {
5726
+ g_localKeyStorage = parseStorageData(`danonicw-${g_keyObj.currentKey}k`, {
5492
5727
  reverse: C_FLG_OFF,
5493
5728
  keyCtrl: [[]],
5494
5729
  keyCtrlPtn: 0,
5495
5730
  setColor: [],
5496
- };
5731
+ });
5497
5732
  storageObj = g_localKeyStorage;
5498
5733
 
5499
5734
  } else {
@@ -5922,7 +6157,7 @@ const createOptionWindow = _sprite => {
5922
6157
 
5923
6158
  const fadeinSlider = document.getElementById(`fadeinSlider`);
5924
6159
  fadeinSlider.addEventListener(`input`, () =>
5925
- g_stateObj.fadein = inputSlider(fadeinSlider, lnkFadein), false);
6160
+ g_stateObj.fadein = inputSlider(fadeinSlider, lnkFadein, `fadein`), false);
5926
6161
 
5927
6162
  // ---------------------------------------------------
5928
6163
  // ボリューム (Volume)
@@ -6629,12 +6864,12 @@ const createSettingsDisplayWindow = _sprite => {
6629
6864
 
6630
6865
  const appearanceSlider = document.getElementById(`appearanceSlider`);
6631
6866
  appearanceSlider.addEventListener(`input`, () =>
6632
- g_hidSudObj.filterPos = inputSliderAppearance(appearanceSlider, lblAppearancePos), false);
6867
+ g_hidSudObj.filterPos = inputSlider(appearanceSlider, lblAppearancePos, `appearance`), false);
6633
6868
 
6634
6869
  const dispAppearanceSlider = () => {
6635
6870
  [`lblAppearanceBar`, `lnkLockBtn`, `lnkfilterLine`].forEach(obj =>
6636
6871
  $id(obj).visibility = g_appearanceRanges.includes(g_stateObj.appearance) ? `Visible` : `Hidden`);
6637
- inputSliderAppearance(appearanceSlider, lblAppearancePos);
6872
+ inputSlider(appearanceSlider, lblAppearancePos, `appearance`);
6638
6873
  };
6639
6874
  dispAppearanceSlider();
6640
6875
 
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * Source by tickle
7
7
  * Created : 2019/11/19
8
- * Revised : 2025/02/11 (v39.4.1)
8
+ * Revised : 2025/02/15 (v39.5.0)
9
9
  *
10
10
  * https://github.com/cwtickle/danoniplus
11
11
  */
@@ -93,6 +93,7 @@ const g_limitObj = {
93
93
 
94
94
  /** 設定項目の位置 */
95
95
  const g_settingPos = {
96
+ dataMgt: {},
96
97
  option: {
97
98
  difficulty: { heightPos: 0, y: -5, dw: 0, dh: 10 },
98
99
  speed: { heightPos: 2, y: 0, dw: 0, dh: 0 },
@@ -167,6 +168,7 @@ const g_windowObj = {
167
168
  divBack: { background: `linear-gradient(#000000, #222222)` },
168
169
 
169
170
  colorPickSprite: { x: 0, y: 90, w: 50, h: 280 },
171
+ keyListSprite: { x: 0, y: g_limitObj.setLblHeight * 7.5 + 40, w: 150, h: 120, overflow: C_DIS_AUTO },
170
172
  };
171
173
 
172
174
  const g_lblPosObj = {};
@@ -239,6 +241,27 @@ const updateWindowSiz = () => {
239
241
  x: g_btnX(1) - 160, y: (g_sHeight / 2) + 150, w: 140, h: 50, siz: 20, border: `solid 1px #999999`,
240
242
  },
241
243
 
244
+ /** データ管理 */
245
+ dataDelMsg: {
246
+ x: 0, y: 65, w: g_sWidth, h: 20, siz: g_limitObj.mainSiz,
247
+ },
248
+ btnResetN: {
249
+ x: g_btnX(1 / 3), y: g_sHeight - 100, w: g_btnWidth(1 / 3), h: g_limitObj.btnHeight,
250
+ },
251
+ btnUndo: {
252
+ x: g_btnX(2 / 3), y: g_sHeight - 100, w: g_btnWidth(1 / 3), h: g_limitObj.btnHeight,
253
+ },
254
+ lblWorkDataView: {
255
+ x: g_btnX(5 / 12), y: 100, w: g_btnWidth(1 / 2), h: g_sHeight / 4, siz: 12, align: C_ALIGN_LEFT,
256
+ overflow: C_DIS_AUTO, background: `#222222`, color: `#cccccc`,
257
+ whiteSpace: `nowrap`,
258
+ },
259
+ lblKeyDataView: {
260
+ x: g_btnX(5 / 12), y: 100 + g_sHeight / 4 + 10, w: g_btnWidth(1 / 2), h: g_sHeight / 3 - 10, siz: 12, align: C_ALIGN_LEFT,
261
+ overflow: C_DIS_AUTO, background: `#222222`, color: `#cccccc`,
262
+ whiteSpace: `nowrap`,
263
+ },
264
+
242
265
  /** 設定画面 */
243
266
  btnBack: {
244
267
  x: g_btnX(),
@@ -274,7 +297,7 @@ const updateWindowSiz = () => {
274
297
  x: g_limitObj.setLblLeft, y: 0,
275
298
  },
276
299
  lblFadeinBar: {
277
- x: g_limitObj.setLblLeft, y: 0,
300
+ x: g_limitObj.setLblLeft, y: 0, type: `range`,
278
301
  },
279
302
 
280
303
  /** 設定: 譜面明細子画面 */
@@ -327,7 +350,7 @@ const updateWindowSiz = () => {
327
350
  x: g_limitObj.setLblLeft, y: 20, siz: 12, align: C_ALIGN_CENTER,
328
351
  },
329
352
  lblAppearanceBar: {
330
- x: g_limitObj.setLblLeft, y: 15,
353
+ x: g_limitObj.setLblLeft, y: 15, type: `range`,
331
354
  },
332
355
  lnkLockBtn: {
333
356
  x: g_limitObj.setLblLeft + g_limitObj.setLblWidth - 40, y: 0, w: 40, h: g_limitObj.setLblHeight, siz: 12,
@@ -940,6 +963,11 @@ const g_stateObj = {
940
963
  rotateEnabled: true,
941
964
  flatStepHeight: C_ARW_WIDTH,
942
965
 
966
+ dm_environment: C_FLG_OFF,
967
+ dm_highscores: C_FLG_OFF,
968
+ dm_customKey: C_FLG_OFF,
969
+ dm_others: C_FLG_OFF,
970
+
943
971
  layerNum: 2,
944
972
  };
945
973
 
@@ -989,6 +1017,16 @@ const makeSpeedList = (_minSpd, _maxSpd) => [...Array((_maxSpd - _minSpd) * 20 +
989
1017
 
990
1018
  // 設定系全般管理
991
1019
  const g_settings = {
1020
+
1021
+ dataMgtNum: {
1022
+ environment: 0,
1023
+ highscores: 0,
1024
+ customKey: 0,
1025
+ others: 0,
1026
+ },
1027
+ environments: [`adjustment`, `volume`, `colorType`, `appearance`, `opacity`, `hitPosition`],
1028
+ keyStorages: [`reverse`, `keyCtrl`, `keyCtrlPtn`, `shuffle`, `color`, `stepRtn`],
1029
+
992
1030
  speeds: makeSpeedList(C_MIN_SPEED, C_MAX_SPEED),
993
1031
  speedNum: 0,
994
1032
  speedTerms: [20, 5, 1],
@@ -1247,6 +1285,24 @@ const resetXY = () => {
1247
1285
  Object.keys(g_posYs).forEach(_id => delete g_posYs[_id]);
1248
1286
  };
1249
1287
 
1288
+ /**
1289
+ * データ消去用管理関数
1290
+ */
1291
+ const g_resetFunc = new Map([
1292
+ ['highscores', () => {
1293
+ delete g_localStorage.highscores;
1294
+ g_localStorage.highscores = {};
1295
+ }],
1296
+ ['environment', () => g_settings.environments.forEach(key => delete g_localStorage[key])],
1297
+ [`customKey`, () => Object.keys(g_localStorage)
1298
+ .filter(key => listMatching(key, g_settings.keyStorages.concat(`setColor`), { prefix: `^` }))
1299
+ .forEach(key => delete g_localStorage[key])],
1300
+ [`others`, () => Object.keys(g_localStorage)
1301
+ .filter(key => !g_settings.environments.includes(key) && key !== `highscores` &&
1302
+ !listMatching(key, g_settings.keyStorages.concat(`setColor`), { prefix: `^` }))
1303
+ .forEach(key => delete g_localStorage[key])],
1304
+ ]);
1305
+
1250
1306
  /**
1251
1307
  * シャッフル適用関数
1252
1308
  * @param {number} keyNum
@@ -1450,6 +1506,11 @@ const g_effectFunc = new Map([
1450
1506
  ['Squids', () => g_setEffect(`effects-squids-arrow`, `effects-squids-frz`)],
1451
1507
  ]);
1452
1508
 
1509
+ const g_sliderView = new Map([
1510
+ ['fadein', _val => `${_val}${g_lblNameObj.percent}`],
1511
+ ['appearance', _val => `${g_hidSudObj.distH[g_stateObj.appearance](_val)}`],
1512
+ ]);
1513
+
1453
1514
  const g_keycons = {
1454
1515
  configTypes: [`Main`, `Replaced`, `ALL`],
1455
1516
  configTypeNum: 0,
@@ -1875,6 +1936,16 @@ const g_shortcutObj = {
1875
1936
  F1: { id: `btnHelp`, reset: true },
1876
1937
  ControlLeft_KeyC: { id: `` },
1877
1938
  KeyC: { id: `btnComment` },
1939
+ KeyD: { id: `btnReset` },
1940
+ },
1941
+ dataMgt: {
1942
+ KeyE: { id: `btnEnvironment` },
1943
+ KeyH: { id: `btnHighscores` },
1944
+ KeyK: { id: `btnCustomKey` },
1945
+ KeyO: { id: `btnOthers` },
1946
+ Escape: { id: `btnBack` },
1947
+ ShiftLeft_Tab: { id: `btnBack` },
1948
+ ShiftRight_Tab: { id: `btnBack` },
1878
1949
  },
1879
1950
  option: {
1880
1951
  ShiftLeft_KeyD: { id: `lnkDifficultyL` },
@@ -2142,6 +2213,8 @@ const g_shortcutObj = {
2142
2213
  Escape: { id: `btnBack` },
2143
2214
  Space: { id: `btnKeyConfig` },
2144
2215
  Enter: { id: `btnPlay` },
2216
+ ShiftLeft_Tab: { id: `btnBack` },
2217
+ ShiftRight_Tab: { id: `btnBack` },
2145
2218
  Tab: { id: `btnexSetting` },
2146
2219
  },
2147
2220
  keyConfig: {
@@ -2173,6 +2246,7 @@ const g_shortcutObj = {
2173
2246
  const g_btnWaitFrame = {
2174
2247
  initial: { b_frame: 0, s_frame: 0 },
2175
2248
  title: { b_frame: 0, s_frame: 0 },
2249
+ dataMgt: { b_frame: 0, s_frame: 0 },
2176
2250
  option: { b_frame: 0, s_frame: 0, initial: true },
2177
2251
  difSelector: { b_frame: 0, s_frame: 0 },
2178
2252
  settingsDisplay: { b_frame: 0, s_frame: 0 },
@@ -2187,6 +2261,7 @@ const g_btnWaitFrame = {
2187
2261
  // 主要ボタンのリスト
2188
2262
  const g_btnPatterns = {
2189
2263
  title: { Start: 0, Comment: -10 },
2264
+ dataMgt: { Back: 0, Environment: -35, Highscores: -35, CustomKey: -35, Others: -35 },
2190
2265
  option: { Back: 0, KeyConfig: 0, Play: 0, Display: -5, Save: -10, Graph: -25 },
2191
2266
  difSelector: {},
2192
2267
  settingsDisplay: { Back: 0, KeyConfig: 0, Play: 0, Save: -10, Settings: -5 },
@@ -3354,7 +3429,7 @@ const g_lblNameObj = {
3354
3429
  maker: `Maker`,
3355
3430
  artist: `Artist`,
3356
3431
 
3357
- dataReset: `Data Reset`,
3432
+ dataReset: `Data Management`,
3358
3433
  dataSave: `Data Save`,
3359
3434
  clickHere: `Click Here!!`,
3360
3435
  comment: `Comment`,
@@ -3366,6 +3441,7 @@ const g_lblNameObj = {
3366
3441
  b_keyConfig: `KeyConfig`,
3367
3442
  b_play: `PLAY!`,
3368
3443
  b_reset: `Reset Key`,
3444
+ b_undo: `Restore`,
3369
3445
  b_settings: `To Settings`,
3370
3446
  b_copy: `CopyResult`,
3371
3447
  b_tweet: `Post X`,
@@ -3607,6 +3683,8 @@ const g_linkObj = {
3607
3683
  */
3608
3684
  const g_lang_lblNameObj = {
3609
3685
  Ja: {
3686
+ dataDeleteDesc: `消去したいデータの種類を選んで「Reset」を押してください`,
3687
+
3610
3688
  kcDesc: `[{0}:スキップ / {1}:(代替キーのみ)キー無効化]`,
3611
3689
  kcShuffleDesc: `番号をクリックでシャッフルグループ、矢印をクリックでカラーグループを変更`,
3612
3690
  kcNoShuffleDesc: `矢印をクリックでカラーグループを変更`,
@@ -3648,6 +3726,8 @@ const g_lang_lblNameObj = {
3648
3726
  securityUrl: `https://github.com/cwtickle/danoniplus/security/policy`,
3649
3727
  },
3650
3728
  En: {
3729
+ dataDeleteDesc: `Select the type of data you wish to delete and press "Reset".`,
3730
+
3651
3731
  kcDesc: `[{0}:Skip / {1}:Key invalidation (Alternate keys only)]`,
3652
3732
  kcShuffleDesc: `Click the number to change the shuffle group, and click the arrow to change the color.`,
3653
3733
  kcNoShuffleDesc: `Click the arrow to change the color group.`,
@@ -3708,7 +3788,14 @@ const g_lang_msgObj = {
3708
3788
  github: `Dancing☆Onigiri (CW Edition)のGitHubページへ移動します。`,
3709
3789
  security: `Dancing☆Onigiri (CW Edition)のサポート情報ページへ移動します。`,
3710
3790
 
3711
- dataResetConfirm: `この作品のローカル設定をクリアします。よろしいですか?\n(ハイスコアやAdjustment等のデータが全てクリアされます)`,
3791
+ environment: `${g_settings.environments.map(v => toCapitalize(v)).join(`, `)}の設定を初期化します。`,
3792
+ highscores: `全譜面のハイスコアを初期化します。\n個別に初期化したい場合はSettings画面より行ってください。`,
3793
+ customKey: `カスタムキーに関する全ての保存データを消去します。\n下記のKeyDataから個別に消去可能できないときに使用してください。`,
3794
+ others: `標準以外に関する保存データを消去します。`,
3795
+ keyTypes: `Key: {0} の保存データ(個別の色設定を除く)を消去します。`,
3796
+
3797
+ dataResetConfirm: `選択したローカル設定をクリアします。よろしいですか?`,
3798
+ dataRestoreConfirm: `ローカル設定を前回の状態に戻します(1回限り)。よろしいですか?\n消去した設定によっては今の設定が上書きされることがあります。`,
3712
3799
  keyResetConfirm: `キーを初期配置に戻します。よろしいですか?`,
3713
3800
  highscResetConfirm: `この譜面のハイスコアを消去します。よろしいですか?`,
3714
3801
  colorCopyConfirm: `フリーズアローの配色を矢印色に置き換えます\n(通常・ヒット時双方を置き換えます)。よろしいですか?`,
@@ -3790,7 +3877,14 @@ const g_lang_msgObj = {
3790
3877
  github: `Go to the GitHub page of Dancing Onigiri "CW Edition".`,
3791
3878
  security: `Go to the support information page for Dancing Onigiri "CW Edition".`,
3792
3879
 
3793
- dataResetConfirm: `Delete the local settings in this game. Is it OK?\n(High score, adjustment, volume and some settings will be initialized)`,
3880
+ environment: `Initialize ${g_settings.environments.map(v => toCapitalize(v)).join(`, `)} settings.`,
3881
+ highscores: `Initializes the high score of all charts. \nIf you want to initialize each chart individually, \nplease do so from the Highscore view in the Settings screen.`,
3882
+ customKey: `Delete stored data related to all custom keymodes. Use this option when you cannot delete individual KeyData from the following KeyData`,
3883
+ others: `Delete non-standard stored data.`,
3884
+ keyTypes: `Deletes the stored data (except color settings) for Key: {0}.`,
3885
+
3886
+ dataResetConfirm: `Delete the selected local settings. Is it OK?`,
3887
+ dataRestoreConfirm: `Restore local settings to previous state (one time only). Is it OK?\nSome deleted settings may overwrite the current settings.`,
3794
3888
  keyResetConfirm: `Resets the assigned key to the initial state. Is it OK?`,
3795
3889
  highscResetConfirm: `Erases the high score for this chart. Is it OK?`,
3796
3890
  colorCopyConfirm: `Replace freeze arrow color scheme with arrow color\n(replace both normal and hit). Is this OK?`,
@@ -3873,8 +3967,10 @@ const g_lang_msgObj = {
3873
3967
  */
3874
3968
  const g_errMsgObj = {
3875
3969
  title: [],
3970
+ dataMgt: [],
3876
3971
  option: [],
3877
3972
  settingsDisplay: [],
3973
+ exSetting: [],
3878
3974
  loading: [],
3879
3975
  main: [],
3880
3976
  result: [],
@@ -3888,6 +3984,7 @@ const g_customJsObj = {
3888
3984
  preTitle: [],
3889
3985
  title: [],
3890
3986
  titleEnterFrame: [],
3987
+ dataMgt: [],
3891
3988
  option: [],
3892
3989
  difficulty: [],
3893
3990
  settingsDisplay: [],
@@ -3927,6 +4024,7 @@ const g_customJsObj = {
3927
4024
  */
3928
4025
  const g_skinJsObj = {
3929
4026
  title: [],
4027
+ dataMgt: [],
3930
4028
  option: [],
3931
4029
  settingsDisplay: [],
3932
4030
  exSetting: [],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "danoniplus",
3
- "version": "39.4.1",
3
+ "version": "39.5.0",
4
4
  "description": "Dancing☆Onigiri (CW Edition) - Web-based Rhythm Game",
5
5
  "main": "index.js",
6
6
  "scripts": {