danoniplus 50.2.0 → 50.3.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/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/09/01
7
+ * Revised : 2026/09/10
8
8
  *
9
9
  * https://github.com/cwtickle/danoniplus
10
10
  */
11
- const g_version = `Ver 50.2.0`;
12
- const g_revisedDate = `2026/09/01`;
11
+ const g_version = `Ver 50.3.1`;
12
+ const g_revisedDate = `2026/09/10`;
13
13
 
14
14
  // カスタム用バージョン (danoni_custom.js 等で指定可)
15
15
  let g_localVersion = ``;
@@ -1757,7 +1757,7 @@ const createColorPicker = (_parentObj, _id, _func, { x = 0, y = 0 } = {}) => {
1757
1757
  picker.style.top = wUnit(y);
1758
1758
  picker.style.position = `absolute`;
1759
1759
  picker.style.pointerEvents = C_DIS_AUTO;
1760
- picker.addEventListener(`change`, _func);
1760
+ g_handler.addListener(picker, `change`, _func);
1761
1761
  _parentObj.appendChild(picker);
1762
1762
  return picker;
1763
1763
  };
@@ -1871,16 +1871,16 @@ const g_handler = (() => {
1871
1871
  * @param {EventTarget} _target
1872
1872
  * @param {string} _type
1873
1873
  * @param {EventListenerOrEventListenerObject} _listener
1874
- * @param {boolean} [_capture=false]
1874
+ * @param {boolean|AddEventListenerOptions} [_options=false]
1875
1875
  * @returns {number}
1876
1876
  */
1877
- addListener: (_target, _type, _listener, _capture = false) => {
1878
- _target.addEventListener(_type, _listener, _capture);
1877
+ addListener: (_target, _type, _listener, _options = false) => {
1878
+ _target.addEventListener(_type, _listener, _options);
1879
1879
  events[key] = {
1880
1880
  target: _target,
1881
1881
  type: _type,
1882
1882
  listener: _listener,
1883
- capture: _capture
1883
+ options: _options // boolean/objectどちらでもそのまま保持
1884
1884
  };
1885
1885
  return key++;
1886
1886
  },
@@ -1891,9 +1891,104 @@ const g_handler = (() => {
1891
1891
  removeListener: key => {
1892
1892
  if (key in events) {
1893
1893
  const e = events[key];
1894
- e.target.removeEventListener(e.type, e.listener, e.capture);
1894
+ e.target.removeEventListener(e.type, e.listener, e.options);
1895
1895
  delete events[key];
1896
1896
  }
1897
+ },
1898
+ /**
1899
+ * すべてのイベントリスナーを削除
1900
+ */
1901
+ removeAll: () => {
1902
+ Object.values(events).forEach(e => {
1903
+ e.target.removeEventListener(e.type, e.listener, e.options);
1904
+ });
1905
+ for (const k in events) delete events[k];
1906
+ },
1907
+ /**
1908
+ * 指定した要素配下(自身含む)のイベントリスナーをまとめて削除
1909
+ * @param {HTMLDivElement} _container
1910
+ */
1911
+ removeByContainer: _container => {
1912
+ Object.entries(events).forEach(([k, e]) => {
1913
+ if (_container.contains(e.target)) {
1914
+ e.target.removeEventListener(e.type, e.listener, e.options);
1915
+ delete events[k];
1916
+ }
1917
+ });
1918
+ }
1919
+ };
1920
+ })();
1921
+
1922
+ // setInterval/setTimeout版
1923
+ const g_timerHandler = (() => {
1924
+ const timers = {}; // key -> { id, type: 'interval' | 'timeout' }
1925
+ let key = 0;
1926
+
1927
+ const clearByType = entry => {
1928
+ if (entry.type === 'interval') {
1929
+ clearInterval(entry.id);
1930
+ } else {
1931
+ clearTimeout(entry.id);
1932
+ }
1933
+ };
1934
+
1935
+ const clear = key => {
1936
+ if (key in timers) {
1937
+ clearByType(timers[key]);
1938
+ delete timers[key];
1939
+ }
1940
+ };
1941
+
1942
+ return {
1943
+ setInterval: (_callback, _ms) => {
1944
+ const id = setInterval(_callback, _ms);
1945
+ timers[key] = { id, type: 'interval' };
1946
+ return key++;
1947
+ },
1948
+ setTimeout: (_callback, _ms, ..._args) => {
1949
+ const id = setTimeout(() => {
1950
+ _callback(..._args);
1951
+ delete timers[myKey]; // 発火し終わったら自動で登録簿から消す
1952
+ }, _ms);
1953
+ const myKey = key;
1954
+ timers[key] = { id, type: 'timeout' };
1955
+ return key++;
1956
+ },
1957
+ clear,
1958
+ clearInterval: clear,
1959
+ clearTimeout: clear,
1960
+ clearAll: () => {
1961
+ Object.values(timers).forEach(clearByType);
1962
+ for (const k in timers) delete timers[k];
1963
+ }
1964
+ };
1965
+ })();
1966
+
1967
+ // requestAnimationFrame版
1968
+ const g_rafHandler = (() => {
1969
+ const loops = {};
1970
+ let key = 0;
1971
+
1972
+ return {
1973
+ start: _callback => {
1974
+ const myKey = key++;
1975
+ function loop() {
1976
+ if (!(myKey in loops)) return; // clearAll済みなら自然停止
1977
+ _callback();
1978
+ loops[myKey] = requestAnimationFrame(loop);
1979
+ }
1980
+ loops[myKey] = requestAnimationFrame(loop);
1981
+ return myKey;
1982
+ },
1983
+ stop: key => {
1984
+ if (key in loops) {
1985
+ cancelAnimationFrame(loops[key]);
1986
+ delete loops[key];
1987
+ }
1988
+ },
1989
+ stopAll: () => {
1990
+ Object.values(loops).forEach(id => cancelAnimationFrame(id));
1991
+ for (const k in loops) delete loops[k];
1897
1992
  }
1898
1993
  };
1899
1994
  })();
@@ -1905,10 +2000,8 @@ const g_handler = (() => {
1905
2000
  const deleteChildspriteAll = _parentObjName => {
1906
2001
 
1907
2002
  const parentsprite = document.getElementById(_parentObjName);
2003
+ g_handler.removeByContainer(parentsprite);
1908
2004
  while (parentsprite.hasChildNodes()) {
1909
- g_handler.removeListener(parentsprite.firstChild.getAttribute(`lsnrkey`));
1910
- g_handler.removeListener(parentsprite.firstChild.getAttribute(`lsnrkeyTS`));
1911
- g_handler.removeListener(parentsprite.firstChild.getAttribute(`lsnrkeyTE`));
1912
2005
  parentsprite.removeChild(parentsprite.firstChild);
1913
2006
  }
1914
2007
  };
@@ -1920,6 +2013,7 @@ const deleteChildspriteAll = _parentObjName => {
1920
2013
  */
1921
2014
  const deleteDiv = (_parentId, _idName) => {
1922
2015
  if (document.getElementById(_idName) !== null) {
2016
+ g_handler.removeByContainer(document.getElementById(_idName));
1923
2017
  _parentId.removeChild(document.getElementById(_idName));
1924
2018
  }
1925
2019
  };
@@ -1969,13 +2063,13 @@ const createCss2Button = (_id, _text, _func = () => true, {
1969
2063
  (g_initialFlg && g_btnWaitFrame[groupName].initial)) {
1970
2064
  } else {
1971
2065
  style.pointerEvents = C_DIS_NONE;
1972
- setTimeout(() => style.pointerEvents = rest.pointerEvents ?? C_DIS_AUTO,
2066
+ g_timerHandler.setTimeout(() => style.pointerEvents = rest.pointerEvents ?? C_DIS_AUTO,
1973
2067
  g_btnWaitFrame[groupName].b_frame * 1000 / g_fps);
1974
2068
  }
1975
2069
  }
1976
2070
 
1977
2071
  // ボタンを押したときの動作
1978
- const lsnrkey = g_handler.addListener(div, `click`, evt => {
2072
+ g_handler.addListener(div, `click`, evt => {
1979
2073
  if (!setBoolVal(g_btnDeleteFlg[_id])) {
1980
2074
  _func(evt);
1981
2075
  }
@@ -2006,9 +2100,6 @@ const createCss2Button = (_id, _text, _func = () => true, {
2006
2100
  return false;
2007
2101
  };
2008
2102
 
2009
- // イベントリスナー用のキーをセット
2010
- div.setAttribute(`lsnrkey`, lsnrkey);
2011
-
2012
2103
  return div;
2013
2104
  };
2014
2105
 
@@ -2788,7 +2879,7 @@ const warmUpAudioContext = async () => {
2788
2879
  const baseTime = ctx.currentTime;
2789
2880
  const limitTime = performance.now() + 500;
2790
2881
  while (ctx.currentTime === baseTime && performance.now() < limitTime) {
2791
- await new Promise(resolve => setTimeout(resolve, 10));
2882
+ await new Promise(resolve => g_timerHandler.setTimeout(resolve, 10));
2792
2883
  }
2793
2884
  };
2794
2885
 
@@ -5905,7 +5996,7 @@ const titleInit = (_initFlg = false) => {
5905
5996
  createDivCss2Label(`lblMusicSelectDetail`, ``, g_lblPosObj.lblMusicSelectDetail),
5906
5997
  createCss2Button(`btnStart`,
5907
5998
  `>`, () => {
5908
- clearTimeout(g_timeoutEvtTitleId);
5999
+ g_timerHandler.clearTimeout(g_timeoutEvtTitleId);
5909
6000
  g_handler.removeListener(wheelHandler);
5910
6001
  g_keyObj.prevKey = `Dummy${g_settings.musicIdxNum}`;
5911
6002
  g_langStorage.bgmVolume = g_stateObj.bgmVolume;
@@ -5992,14 +6083,14 @@ const titleInit = (_initFlg = false) => {
5992
6083
 
5993
6084
  let spriteOpacity = 1;
5994
6085
  let fadeOpacity = null;
5995
- const fadeStartOpacity = setTimeout(() => {
5996
- clearTimeout(fadeStartOpacity);
6086
+ const fadeStartOpacity = g_timerHandler.setTimeout(() => {
6087
+ g_timerHandler.clearTimeout(fadeStartOpacity);
5997
6088
  setOpacity(spriteOpacity);
5998
6089
  }, 2000);
5999
6090
 
6000
6091
  const setOpacity = (_opacity) => {
6001
6092
  if (_opacity <= 0) {
6002
- clearTimeout(fadeOpacity);
6093
+ g_timerHandler.clearTimeout(fadeOpacity);
6003
6094
  mSelectTitleSprite.style.display = C_DIS_NONE;
6004
6095
  if (!g_stateObj.bgmMuteFlg && g_audioForMS) {
6005
6096
  g_audioForMS.muted = false;
@@ -6012,7 +6103,7 @@ const titleInit = (_initFlg = false) => {
6012
6103
  }
6013
6104
  } else {
6014
6105
  mSelectTitleSprite.style.opacity = _opacity;
6015
- fadeOpacity = setTimeout(() => {
6106
+ fadeOpacity = g_timerHandler.setTimeout(() => {
6016
6107
  spriteOpacity -= 0.25;
6017
6108
  setOpacity(spriteOpacity);
6018
6109
  }, 50);
@@ -6074,7 +6165,7 @@ const titleInit = (_initFlg = false) => {
6074
6165
 
6075
6166
  // Click Here
6076
6167
  createCss2Button(`btnStart`, g_lblNameObj.clickHere, () => {
6077
- clearTimeout(g_timeoutEvtTitleId);
6168
+ g_timerHandler.clearTimeout(g_timeoutEvtTitleId);
6078
6169
  g_keyObj.prevKey = `Dummy${g_settings.musicIdxNum}`;
6079
6170
  }, {
6080
6171
  x: g_btnX(), w: g_btnWidth(), siz: g_limitObj.titleSiz, resetFunc: () => optionInit(),
@@ -6093,7 +6184,7 @@ const titleInit = (_initFlg = false) => {
6093
6184
 
6094
6185
  // Reset
6095
6186
  createCss2Button(`btnReset`, g_lblNameObj.dataReset, () => {
6096
- clearTimeout(g_timeoutEvtTitleId);
6187
+ g_timerHandler.clearTimeout(g_timeoutEvtTitleId);
6097
6188
  g_handler.removeListener(wheelHandler);
6098
6189
  dataMgtInit();
6099
6190
  }, g_lblPosObj.btnReset, g_cssObj.button_Reset),
@@ -6173,10 +6264,10 @@ const titleInit = (_initFlg = false) => {
6173
6264
 
6174
6265
  g_scoreObj.titleFrameNum++;
6175
6266
  g_animationData.forEach(sprite => g_scoreObj[`${sprite}TitleFrameNum`]++);
6176
- g_timeoutEvtTitleId = setTimeout(flowTitleTimeline, 1000 / g_fps - buffTime);
6267
+ g_timeoutEvtTitleId = g_timerHandler.setTimeout(flowTitleTimeline, 1000 / g_fps - buffTime);
6177
6268
  };
6178
6269
 
6179
- g_timeoutEvtTitleId = setTimeout(flowTitleTimeline, 1000 / g_fps);
6270
+ g_timeoutEvtTitleId = g_timerHandler.setTimeout(flowTitleTimeline, 1000 / g_fps);
6180
6271
 
6181
6272
  // キー操作イベント(デフォルト)
6182
6273
  setShortcutEvent(g_currentPage, () => true, { dfEvtFlg: true });
@@ -6315,7 +6406,7 @@ const pauseBGM = () => {
6315
6406
  }
6316
6407
  [`bgmLooped`, `bgmFadeIn`, `bgmFadeOut`].forEach(id => {
6317
6408
  if (g_stateObj[id]) {
6318
- clearTimeout(g_stateObj[id]);
6409
+ g_timerHandler.clearTimeout(g_stateObj[id]);
6319
6410
  g_stateObj[id] = null;
6320
6411
  }
6321
6412
  });
@@ -6383,10 +6474,10 @@ const playBGM = async (_num, _currentLoopNum = g_settings.musicLoopNum) => {
6383
6474
  g_audioForMS.volume = Math.min(Math.max(volume, 0), 1);
6384
6475
 
6385
6476
  // 次のステップへ
6386
- setTimeout(stepFunc, FADE_INTERVAL_MS);
6477
+ g_timerHandler.setTimeout(stepFunc, FADE_INTERVAL_MS);
6387
6478
  };
6388
6479
 
6389
- return setTimeout(stepFunc, FADE_INTERVAL_MS);
6480
+ return g_timerHandler.setTimeout(stepFunc, FADE_INTERVAL_MS);
6390
6481
  };
6391
6482
 
6392
6483
  /**
@@ -6411,10 +6502,10 @@ const playBGM = async (_num, _currentLoopNum = g_settings.musicLoopNum) => {
6411
6502
  }
6412
6503
 
6413
6504
  // 次のチェックへ
6414
- setTimeout(step, FADE_INTERVAL_MS);
6505
+ g_timerHandler.setTimeout(step, FADE_INTERVAL_MS);
6415
6506
  };
6416
6507
 
6417
- return setTimeout(step, FADE_INTERVAL_MS);
6508
+ return g_timerHandler.setTimeout(step, FADE_INTERVAL_MS);
6418
6509
  };
6419
6510
 
6420
6511
  /**
@@ -6438,7 +6529,7 @@ const playBGM = async (_num, _currentLoopNum = g_settings.musicLoopNum) => {
6438
6529
  g_audioForMS.currentTime = musicStart;
6439
6530
 
6440
6531
  if (isTitle()) {
6441
- setTimeout(() => {
6532
+ g_timerHandler.setTimeout(() => {
6442
6533
  fadeIn();
6443
6534
  if (encodeFlg) repeatBGM();
6444
6535
  }, FADE_DELAY_MS);
@@ -6700,7 +6791,7 @@ const changeMSelect = (_num, _initFlg = false) => {
6700
6791
  if (_initFlg) {
6701
6792
  playBGM(_num);
6702
6793
  } else {
6703
- setTimeout(() => {
6794
+ g_timerHandler.setTimeout(() => {
6704
6795
  if (currentLoopNum === g_settings.musicLoopNum) {
6705
6796
  playBGM(_num, currentLoopNum);
6706
6797
  }
@@ -8891,7 +8982,7 @@ const createOptionWindow = _sprite => {
8891
8982
  );
8892
8983
 
8893
8984
  const fadeinSlider = document.getElementById(`fadeinSlider`);
8894
- fadeinSlider.addEventListener(`input`, () => {
8985
+ g_handler.addListener(fadeinSlider, `input`, () => {
8895
8986
  g_stateObj.fadein = inputSlider(fadeinSlider, lnkFadein, `fadein`);
8896
8987
  updateSettingSummary();
8897
8988
  drawMinimap(g_stateObj.scoreId, { _fadeinFlg: true });
@@ -9516,9 +9607,6 @@ const settingsDisplayInit = () => {
9516
9607
  /** プレビューウィンドウのルートdiv */
9517
9608
  let g_previewRoot = null;
9518
9609
 
9519
- /** プレビューで登録した一時リスナー群 */
9520
- let g_previewLsnrKeys = new Set();
9521
-
9522
9610
  /** プレビュー内の各UIオブジェクトの現在座標 */
9523
9611
  const g_previewPos = {
9524
9612
  arrowJdg: { x: null, y: null }, // 通常判定キャラクタ・コンボ
@@ -9526,15 +9614,6 @@ const g_previewPos = {
9526
9614
  shortcut: { x: null, y: null },
9527
9615
  };
9528
9616
 
9529
- /**
9530
- * プレビュー用リスナー登録(キーをレジストリへ格納)
9531
- */
9532
- const addPreviewListener = (target, type, listener, capture = false) => {
9533
- const key = g_handler.addListener(target, type, listener, capture);
9534
- g_previewLsnrKeys.add(key);
9535
- return key;
9536
- };
9537
-
9538
9617
  /**
9539
9618
  * プレビューのトグル(表示 / 非表示)
9540
9619
  */
@@ -9622,11 +9701,6 @@ const closeDisplayPreview = () => {
9622
9701
  deleteChildspriteAll(`displayPreviewOverlay`);
9623
9702
  overlay.remove();
9624
9703
  }
9625
- // プレビュー専用に登録した残りのハンドラを明示解除
9626
- if (g_previewLsnrKeys?.size) {
9627
- g_previewLsnrKeys.forEach(k => g_handler.removeListener(k));
9628
- g_previewLsnrKeys.clear();
9629
- }
9630
9704
  g_previewRoot = null;
9631
9705
  };
9632
9706
 
@@ -9975,7 +10049,7 @@ const makeElementDraggable = (_target, _key, _playW, _playH, _bounds, _config) =
9975
10049
  background: `rgba(${bgColor},0.04)`,
9976
10050
  });
9977
10051
 
9978
- const keyDown = addPreviewListener(_target, `pointerdown`, _evt => {
10052
+ g_handler.addListener(_target, `pointerdown`, _evt => {
9979
10053
  dragging = true;
9980
10054
  dragStartX = _evt.clientX;
9981
10055
  dragStartY = _evt.clientY;
@@ -9986,7 +10060,7 @@ const makeElementDraggable = (_target, _key, _playW, _playH, _bounds, _config) =
9986
10060
  _evt.stopPropagation();
9987
10061
  });
9988
10062
 
9989
- const keyMove = addPreviewListener(_target, `pointermove`, _evt => {
10063
+ g_handler.addListener(_target, `pointermove`, _evt => {
9990
10064
  if (!dragging) return;
9991
10065
 
9992
10066
  // 1. マウスの実際の移動量を計算
@@ -10007,7 +10081,7 @@ const makeElementDraggable = (_target, _key, _playW, _playH, _bounds, _config) =
10007
10081
  _evt.stopPropagation();
10008
10082
  });
10009
10083
 
10010
- const keyUp = addPreviewListener(_target, `pointerup`, _evt => {
10084
+ g_handler.addListener(_target, `pointerup`, _evt => {
10011
10085
  if (!dragging) return;
10012
10086
  dragging = false;
10013
10087
  _target.style.cursor = `grab`;
@@ -10025,15 +10099,10 @@ const makeElementDraggable = (_target, _key, _playW, _playH, _bounds, _config) =
10025
10099
  _evt.stopPropagation();
10026
10100
  });
10027
10101
 
10028
- addPreviewListener(_target, `pointercancel`, _evt => {
10102
+ g_handler.addListener(_target, `pointercancel`, _evt => {
10029
10103
  dragging = false;
10030
10104
  _target.style.cursor = `grab`;
10031
10105
  });
10032
-
10033
- // 既存の管理用属性(必要に応じて)
10034
- _target.setAttribute(`lsnrkey`, keyMove);
10035
- _target.setAttribute(`lsnrkeyTS`, keyDown);
10036
- _target.setAttribute(`lsnrkeyTE`, keyUp);
10037
10106
  };
10038
10107
 
10039
10108
  /**
@@ -10087,8 +10156,8 @@ const showToast = _msg => {
10087
10156
  opacity: `1`,
10088
10157
  });
10089
10158
  divRoot.appendChild(toast);
10090
- setTimeout(() => { toast.style.opacity = `0`; }, 2200);
10091
- setTimeout(() => { if (toast.parentNode) toast.remove(); }, 2700);
10159
+ g_timerHandler.setTimeout(() => { toast.style.opacity = `0`; }, 2200);
10160
+ g_timerHandler.setTimeout(() => { if (toast.parentNode) toast.remove(); }, 2700);
10092
10161
  };
10093
10162
 
10094
10163
  /**
@@ -10212,7 +10281,7 @@ const createSettingsDisplayWindow = _sprite => {
10212
10281
  };
10213
10282
 
10214
10283
  const appearanceSlider = document.getElementById(`appearanceSlider`);
10215
- appearanceSlider.addEventListener(`input`, () => {
10284
+ g_handler.addListener(appearanceSlider, `input`, () => {
10216
10285
  g_hidSudObj.filterPos = inputSlider(appearanceSlider, lblAppearancePos, `appearance`);
10217
10286
  updateSettingSummary();
10218
10287
  }, false);
@@ -12501,8 +12570,8 @@ const fetchMusicBlobUrl = (_url, _lblLoading) => new Promise((resolve, reject) =
12501
12570
 
12502
12571
  // 停滞タイマーをリセット
12503
12572
  const resetStallTimer = () => {
12504
- clearTimeout(stallTimer);
12505
- stallTimer = setTimeout(() => {
12573
+ g_timerHandler.clearTimeout(stallTimer);
12574
+ stallTimer = g_timerHandler.setTimeout(() => {
12506
12575
  request.abort();
12507
12576
  makeWarningWindow(g_msgInfoObj.E_0033, { backBtnUse: true });
12508
12577
  reject(new Error(`stalled`));
@@ -12511,7 +12580,7 @@ const fetchMusicBlobUrl = (_url, _lblLoading) => new Promise((resolve, reject) =
12511
12580
 
12512
12581
  // 読み込み完了時
12513
12582
  request.addEventListener(`load`, () => {
12514
- clearTimeout(stallTimer);
12583
+ g_timerHandler.clearTimeout(stallTimer);
12515
12584
  if (request.status >= 200 && request.status < 300) {
12516
12585
  const blobUrl = URL.createObjectURL(request.response);
12517
12586
  createEmptySprite(divRoot, `loader`, g_windowObj.loader);
@@ -12541,7 +12610,7 @@ const fetchMusicBlobUrl = (_url, _lblLoading) => new Promise((resolve, reject) =
12541
12610
  });
12542
12611
 
12543
12612
  request.addEventListener(`error`, () => {
12544
- clearTimeout(stallTimer);
12613
+ g_timerHandler.clearTimeout(stallTimer);
12545
12614
  makeWarningWindow(g_msgInfoObj.E_0034, { backBtnUse: true });
12546
12615
  reject(new Error(`network error`));
12547
12616
  });
@@ -14816,7 +14885,7 @@ const getArrowSettings = () => {
14816
14885
  g_workObj.frzReturnFlg = false;
14817
14886
  g_workObj.frzReturnSeq = g_frzReturnSeqFunc.get(g_stateObj.frzReturnType)();
14818
14887
  if (g_workObj.frzReturnTimerId) {
14819
- clearTimeout(g_workObj.frzReturnTimerId);
14888
+ g_timerHandler.clearTimeout(g_workObj.frzReturnTimerId);
14820
14889
  g_workObj.frzReturnTimerId = null;
14821
14890
  }
14822
14891
 
@@ -15468,10 +15537,11 @@ const mainInit = () => {
15468
15537
  isPaused ? resumeTimeline(true) : pauseTimeline(true);
15469
15538
  return blockCode(setCode);
15470
15539
  }
15540
+ // ポーズ中でも押下状態バッファ自体は常に最新化しておく
15541
+ g_inputKeyBuffer[setCode] = true;
15471
15542
  if (isPaused) {
15472
15543
  return blockCode(setCode);
15473
15544
  }
15474
- g_inputKeyBuffer[setCode] = true;
15475
15545
  mainKeyDownActFunc[g_stateObj.autoAll](setCode);
15476
15546
 
15477
15547
  // 曲中リトライ、タイトルバック
@@ -15480,7 +15550,7 @@ const mainInit = () => {
15480
15550
  if (g_isMac && keyIsShift()) {
15481
15551
  // Mac OS、IPad OSはDeleteキーが無いためShift+BSで代用
15482
15552
  g_audio.pause();
15483
- clearTimeout(g_timeoutEvtId);
15553
+ g_timerHandler.clearTimeout(g_timeoutEvtId);
15484
15554
  titleInit();
15485
15555
 
15486
15556
  } else {
@@ -15490,7 +15560,7 @@ const mainInit = () => {
15490
15560
 
15491
15561
  } else if (setCode === g_kCdN[g_headerObj.keyTitleBack]) {
15492
15562
  g_audio.pause();
15493
- clearTimeout(g_timeoutEvtId);
15563
+ g_timerHandler.clearTimeout(g_timeoutEvtId);
15494
15564
  if (keyIsShift()) {
15495
15565
  if (g_currentArrows !== g_fullArrows || g_stateObj.lifeMode === C_LFE_BORDER && g_workObj.lifeVal < g_workObj.lifeBorder) {
15496
15566
  g_gameOverFlg = true;
@@ -15898,6 +15968,10 @@ const mainInit = () => {
15898
15968
  // 矢印色の設定
15899
15969
  // - 枠/塗りつぶし色: g_attrObj[arrowName].Arrow / ArrowShadow
15900
15970
  g_typeLists.arrowColor.forEach(val => g_attrObj[arrowName][`Arrow${val}`] = g_workObj[`${_name}${val}Colors`][_j]);
15971
+
15972
+ // g_attrObj定義後のカスタムイベント
15973
+ safeExecuteCustomHooks(`g_customJsObj.preMakeArrow`, g_customJsObj.preMakeArrow, _attrs, arrowName, _name, _arrowCnt);
15974
+
15901
15975
  arrowSprite[g_workObj.dividePos[_j]].appendChild(arrowRoot);
15902
15976
  const arrowSubRoot = createEmptySprite(arrowRoot, `sub${arrowName}`, { x: 0, y: 0, w: C_ARW_WIDTH, h: C_ARW_WIDTH });
15903
15977
 
@@ -16038,6 +16112,10 @@ const mainInit = () => {
16038
16112
  g_attrObj[frzName][`${val}All`] = g_workObj[`${_name}${val}ColorsAll`][_j];
16039
16113
  }
16040
16114
  });
16115
+
16116
+ // g_attrObj定義後のカスタムイベント
16117
+ safeExecuteCustomHooks(`g_customJsObj.preMakeFrzArrow`, g_customJsObj.preMakeFrzArrow, _attrs, frzName, _name, _arrowCnt);
16118
+
16041
16119
  arrowSprite[g_workObj.dividePos[_j]].appendChild(frzRoot);
16042
16120
  let shadowColor = _shadowColor === `Default` ? _normalColor : _shadowColor;
16043
16121
  const frzSubRoot = createEmptySprite(frzRoot, `sub${frzName}`, { x: 0, y: 0, w: C_ARW_WIDTH, h: C_ARW_WIDTH + firstBarLength });
@@ -16376,13 +16454,18 @@ const mainInit = () => {
16376
16454
  }
16377
16455
  });
16378
16456
 
16457
+ // ユーザカスタムイベント(フレーム毎、タイマー直前)
16458
+ safeExecuteCustomHooks(`g_customJsObj.mainBeforeFrameTimer`, g_customJsObj.mainBeforeFrameTimer, {
16459
+ arrowCnts, frzCnts, dummyArrowCnts, dummyFrzCnts,
16460
+ });
16461
+
16379
16462
  // 曲終了判定
16380
16463
  if (currentFrame >= fullFrame) {
16381
16464
  if (g_stateObj.lifeMode === C_LFE_BORDER && g_workObj.lifeVal < g_workObj.lifeBorder) {
16382
16465
  g_gameOverFlg = true;
16383
16466
  }
16384
16467
  resetKeyControl();
16385
- clearTimeout(g_timeoutEvtId);
16468
+ g_timerHandler.clearTimeout(g_timeoutEvtId);
16386
16469
  g_workObj.mainEndTime = thisTime;
16387
16470
  resultInit();
16388
16471
 
@@ -16390,7 +16473,7 @@ const mainInit = () => {
16390
16473
 
16391
16474
  // ライフ制&ライフ0の場合は途中終了
16392
16475
  g_audio.pause();
16393
- clearTimeout(g_timeoutEvtId);
16476
+ g_timerHandler.clearTimeout(g_timeoutEvtId);
16394
16477
  g_gameOverFlg = true;
16395
16478
  g_finishFlg = false;
16396
16479
  resultInit();
@@ -16429,7 +16512,7 @@ const mainInit = () => {
16429
16512
  g_scoreObj.frameNum++;
16430
16513
  g_scoreObj.baseFrame++;
16431
16514
  }
16432
- g_timeoutEvtId = setTimeout(flowTimeline, holdFrame ? g_maxFrameWait :
16515
+ g_timeoutEvtId = g_timerHandler.setTimeout(flowTimeline, holdFrame ? g_maxFrameWait :
16433
16516
  Math.min(Math.max(1000 / g_fps - buffTime, 0), g_maxFrameWait));
16434
16517
  }
16435
16518
  };
@@ -16443,7 +16526,7 @@ const mainInit = () => {
16443
16526
  */
16444
16527
  const cancelResumeCountdown = () => {
16445
16528
  if (countdownTimeoutId !== null) {
16446
- clearTimeout(countdownTimeoutId);
16529
+ g_timerHandler.clearTimeout(countdownTimeoutId);
16447
16530
  countdownTimeoutId = null;
16448
16531
  }
16449
16532
  document.getElementById(`lblResumeCountdown`)?.remove();
@@ -16492,15 +16575,15 @@ const mainInit = () => {
16492
16575
  // 一時停止時点で早期終了させる(位置復元はせず、通常終了時と同じ後片付けを行う)
16493
16576
  resetFrzReturn();
16494
16577
  }
16495
- clearTimeout(g_timeoutEvtId);
16578
+ g_timerHandler.clearTimeout(g_timeoutEvtId);
16496
16579
  g_audio.pause();
16497
16580
 
16498
- // フォーカスを失うとkeyupが届かなくなり、押しっぱなし判定・表示が残り得るため、
16499
- // ここで強制的に全キー「離した」状態に戻す
16500
- g_inputKeyBuffer = {};
16501
- g_workObj.keyHitFlg.forEach(lane => lane.fill(false));
16502
- mainKeyUpActFunc[g_stateObj.autoAll]();
16503
- divRoot.classList.add(`gamePaused`);
16581
+ // 自動ポーズ(タブ非表示等)の場合のみ強制的に全キー「離した」状態に戻す
16582
+ if (!_manual) {
16583
+ g_inputKeyBuffer = {};
16584
+ g_workObj.keyHitFlg.forEach(lane => lane.fill(false));
16585
+ mainKeyUpActFunc[g_stateObj.autoAll]();
16586
+ }
16504
16587
  };
16505
16588
 
16506
16589
  const resumeTimeline = (_manual = false) => {
@@ -16565,7 +16648,7 @@ const mainInit = () => {
16565
16648
  manualPauseFlg = false;
16566
16649
  pausedElapsedTime = null;
16567
16650
  pausedStartAdjustment = null;
16568
- g_timeoutEvtId = setTimeout(flowTimeline, 1000 / g_fps);
16651
+ g_timeoutEvtId = g_timerHandler.setTimeout(flowTimeline, 1000 / g_fps);
16569
16652
  };
16570
16653
 
16571
16654
  const tick = _remaining => {
@@ -16574,7 +16657,7 @@ const mainInit = () => {
16574
16657
  return;
16575
16658
  }
16576
16659
  countdownLabel.innerHTML = String(_remaining);
16577
- countdownTimeoutId = setTimeout(() => tick(_remaining - 1), 1000);
16660
+ countdownTimeoutId = g_timerHandler.setTimeout(() => tick(_remaining - 1), 1000);
16578
16661
  };
16579
16662
  tick(3);
16580
16663
  };
@@ -16601,7 +16684,7 @@ const mainInit = () => {
16601
16684
  if (document.hidden) {
16602
16685
  pauseTimeline();
16603
16686
  } else {
16604
- g_timeoutEvtId = setTimeout(flowTimeline, 1000 / g_fps);
16687
+ g_timeoutEvtId = g_timerHandler.setTimeout(flowTimeline, 1000 / g_fps);
16605
16688
  }
16606
16689
  };
16607
16690
 
@@ -16865,7 +16948,7 @@ const appearKeyTypes = (_j, _targets, _alphas = fillArray(_targets.length, 1)) =
16865
16948
  const startFrzReturn = () => {
16866
16949
  if (!g_workObj.frzReturnFlg) {
16867
16950
  if (g_workObj.frzReturnTimerId) {
16868
- clearTimeout(g_workObj.frzReturnTimerId);
16951
+ g_timerHandler.clearTimeout(g_workObj.frzReturnTimerId);
16869
16952
  g_workObj.frzReturnTimerId = null;
16870
16953
  }
16871
16954
  lifeBarFrz.classList.remove(g_cssObj.life_frzNormal, g_cssObj.life_frzActive);
@@ -16883,7 +16966,7 @@ const startFrzReturn = () => {
16883
16966
  */
16884
16967
  const resetFrzReturn = () => {
16885
16968
  if (g_workObj.frzReturnTimerId) {
16886
- clearTimeout(g_workObj.frzReturnTimerId);
16969
+ g_timerHandler.clearTimeout(g_workObj.frzReturnTimerId);
16887
16970
  }
16888
16971
  g_workObj.frzReturnTimerId = null;
16889
16972
  g_workObj.frzReturnFlg = false;
@@ -16955,7 +17038,7 @@ const executeFrzReturn = (_seq, _idx, _axis) => {
16955
17038
 
16956
17039
  addTransform(`mainSprite`, `frzReturn`, _transform, g_transPriority.frzReturn);
16957
17040
 
16958
- g_workObj.frzReturnTimerId = setTimeout(() => executeFrzReturn(_seq, _idx + 1, _axis), 20);
17041
+ g_workObj.frzReturnTimerId = g_timerHandler.setTimeout(() => executeFrzReturn(_seq, _idx + 1, _axis), 20);
16959
17042
  };
16960
17043
 
16961
17044
  /**
@@ -16971,7 +17054,7 @@ const executeRetry = async (_logLabel = `Retry`) => {
16971
17054
  g_retryInProgress = true;
16972
17055
  try {
16973
17056
  g_audio.pause();
16974
- clearTimeout(g_timeoutEvtId);
17057
+ g_timerHandler.clearTimeout(g_timeoutEvtId);
16975
17058
  clearWindow(`Main`);
16976
17059
  await musicAfterLoaded();
16977
17060
  await loadChartFile();
@@ -16994,7 +17077,7 @@ const quickRetry = (_retryCondition) => {
16994
17077
  return;
16995
17078
  }
16996
17079
  if (g_settings.autoRetryNum >= retryNum && !g_retryInProgress) {
16997
- setTimeout(async () => {
17080
+ g_timerHandler.setTimeout(async () => {
16998
17081
  await executeRetry(`AutoRetry`);
16999
17082
  }, 16);
17000
17083
  }
@@ -18290,8 +18373,8 @@ const resultInit = () => {
18290
18373
  if (g_finishFlg) {
18291
18374
  g_audio.pause();
18292
18375
  }
18293
- clearTimeout(g_timeoutEvtId);
18294
- clearTimeout(g_timeoutEvtResultId);
18376
+ g_timerHandler.clearTimeout(g_timeoutEvtId);
18377
+ g_timerHandler.clearTimeout(g_timeoutEvtResultId);
18295
18378
  }, { ..._posObj, resetFunc: () => _func() }, _cssClass);
18296
18379
 
18297
18380
  /**
@@ -18360,14 +18443,14 @@ const resultInit = () => {
18360
18443
  // リザルト画面移行後のフェードアウト処理
18361
18444
  if (g_scoreObj.fadeOutFrame >= g_scoreObj.frameNum) {
18362
18445
  if (g_scoreObj.frameNum >= g_scoreObj.fullFrame) {
18363
- clearTimeout(g_timeoutEvtId);
18446
+ g_timerHandler.clearTimeout(g_timeoutEvtId);
18364
18447
  }
18365
18448
  g_scoreObj.frameNum++;
18366
18449
  } else {
18367
18450
  const tmpVolume = (g_audio.volume - (3 * g_stateObj.volume / 100 * C_FRM_AFTERFADE / g_scoreObj.fadeOutTerm) / 1000);
18368
18451
  if (tmpVolume < 0) {
18369
18452
  g_audio.volume = 0;
18370
- clearTimeout(g_timeoutEvtId);
18453
+ g_timerHandler.clearTimeout(g_timeoutEvtId);
18371
18454
  } else {
18372
18455
  g_audio.volume = tmpVolume;
18373
18456
  }
@@ -18378,7 +18461,7 @@ const resultInit = () => {
18378
18461
 
18379
18462
  g_scoreObj.resultFrameNum++;
18380
18463
  g_animationData.forEach(sprite => g_scoreObj[`${sprite}ResultFrameNum`]++);
18381
- g_timeoutEvtResultId = setTimeout(flowResultTimeline, 1000 / g_fps - buffTime);
18464
+ g_timeoutEvtResultId = g_timerHandler.setTimeout(flowResultTimeline, 1000 / g_fps - buffTime);
18382
18465
  };
18383
18466
  flowResultTimeline();
18384
18467
 
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * Source by tickle
7
7
  * Created : 2019/11/19
8
- * Revised : 2026/09/01 (v50.2.0)
8
+ * Revised : 2026/09/08 (v50.3.0)
9
9
  *
10
10
  * https://github.com/cwtickle/danoniplus
11
11
  */
@@ -5244,6 +5244,8 @@ const g_customJsObj = {
5244
5244
  progress: [],
5245
5245
  main: [],
5246
5246
 
5247
+ preMakeArrow: [],
5248
+ preMakeFrzArrow: [],
5247
5249
  makeArrow: [],
5248
5250
  makeFrzArrow: [],
5249
5251
 
@@ -5265,6 +5267,7 @@ const g_customJsObj = {
5265
5267
  judg_dummyFrzHit: [],
5266
5268
 
5267
5269
  mainEnterFrame: [],
5270
+ mainBeforeFrameTimer: [],
5268
5271
  result: [],
5269
5272
  resultEnterFrame: [],
5270
5273
  };
@@ -40,6 +40,16 @@ g_customJsObj.titleEnterFrame.push(() => {
40
40
 
41
41
  });
42
42
 
43
+ /**
44
+ * 選曲変更時(選曲モード時のみ)
45
+ * - この設定は譜面ヘッダーの|packageName=パッケージ名|を指定した場合のみ利用可能です。
46
+ * - 曲を変更したときに呼び出されます。
47
+ * @param {number} _musicIdxNum 選択曲番号
48
+ */
49
+ g_customJsObj.musicSelect.push((_musicIdxNum) => {
50
+
51
+ });
52
+
43
53
  /**
44
54
  * データ管理画面 [Scene: Data Management / Pear]
45
55
  */
@@ -55,7 +65,7 @@ g_customJsObj.precondition.push(() => {
55
65
  });
56
66
 
57
67
  /**
58
- * オプション画面(初期表示) [Scene: Option / Lime]
68
+ * Settings画面(初期表示) [Scene: Option / Lime]
59
69
  */
60
70
  g_customJsObj.option.push(() => {
61
71
 
@@ -71,19 +81,36 @@ g_customJsObj.difficulty.push((_initFlg, _canLoadDifInfoFlg) => {
71
81
  });
72
82
 
73
83
  /**
74
- * 表示変更(初期表示) [Scene: Settings-Display / Lemon]
84
+ * Display画面(初期表示) [Scene: Settings-Display / Lemon]
75
85
  */
76
86
  g_customJsObj.settingsDisplay.push(() => {
77
87
 
78
88
  });
79
89
 
80
90
  /**
81
- * 表示変更(初期表示) [Scene: Ex-Settings / apple]
91
+ * Display画面(プレビュー表示) [Scene: Settings-Display / Lemon]
92
+ * @param {object} _parent Display設定のプレビューの親要素
93
+ * @param {number} _playingWidth プレイ画面の横幅(px)
94
+ * @param {number} _playingHeight プレイ画面の高さ(px)
95
+ */
96
+ g_customJsObj.displayPreview.push((_parent, _playingWidth, _playingHeight) => {
97
+
98
+ });
99
+
100
+ /**
101
+ * Ex-Settings画面(初期表示) [Scene: Ex-Settings / apple]
82
102
  */
83
103
  g_customJsObj.exSetting.push(() => {
84
104
 
85
105
  });
86
106
 
107
+ /**
108
+ * 設定サマリー表示描画時
109
+ */
110
+ g_customJsObj.settingSummary.push(() => {
111
+
112
+ });
113
+
87
114
  /**
88
115
  * キーコンフィグ画面(初期表示) [Scene: KeyConfig / Orange]
89
116
  */
@@ -125,98 +152,152 @@ g_customJsObj.main.push(() => {
125
152
  });
126
153
 
127
154
  /**
128
- * 矢印生成
155
+ * 矢印生成前(g_attrObj定義後)
156
+ * - g_attrObjの書き換えを行いたいときなどの使用を想定
129
157
  * @param {object} _attrs 矢印属性
130
158
  * @param {string} _arrowName 矢印名
131
159
  * @param {string} _name 矢印識別名
132
160
  * @param {number} _arrowCnt 矢印番号
133
161
  */
134
- g_customJsObj.makeArrow.push((_attrs, _arrowName, _name, _arrowCnt) => {
162
+ g_customJsObj.preMakeArrow.push((_attrs, _arrowName, _name, _arrowCnt) => {
135
163
 
136
164
  });
137
165
 
138
166
  /**
139
- * フリーズアロー生成
167
+ * 矢印生成
140
168
  * @param {object} _attrs 矢印属性
141
169
  * @param {string} _arrowName 矢印名
142
170
  * @param {string} _name 矢印識別名
143
171
  * @param {number} _arrowCnt 矢印番号
144
172
  */
173
+ g_customJsObj.makeArrow.push((_attrs, _arrowName, _name, _arrowCnt) => {
174
+
175
+ });
176
+
177
+ /**
178
+ * フリーズアロー生成前(g_attrObj定義後)
179
+ * - g_attrObjの書き換えを行いたいときなどの使用を想定
180
+ * @param {object} _attrs フリーズアロー属性
181
+ * @param {string} _arrowName フリーズアロー名
182
+ * @param {string} _name フリーズアロー識別名
183
+ * @param {number} _arrowCnt フリーズアロー番号
184
+ */
185
+ g_customJsObj.preMakeFrzArrow.push((_attrs, _arrowName, _name, _arrowCnt) => {
186
+
187
+ });
188
+
189
+ /**
190
+ * フリーズアロー生成
191
+ * @param {object} _attrs フリーズアロー属性
192
+ * @param {string} _arrowName フリーズアロー名
193
+ * @param {string} _name フリーズアロー識別名
194
+ * @param {number} _arrowCnt フリーズアロー番号
195
+ */
145
196
  g_customJsObj.makeFrzArrow.push((_attrs, _arrowName, _name, _arrowCnt) => {
146
197
 
147
198
  });
148
199
 
149
200
  /**
150
201
  * ダミー矢印判定時
202
+ * @param {number} _j レーン番号
151
203
  */
152
- g_customJsObj.dummyArrow.push(() => {
204
+ g_customJsObj.dummyArrow.push((_j) => {
153
205
 
154
206
  });
155
207
 
156
208
  /**
157
209
  * ダミーフリーズアロー判定時
210
+ * @param {number} _j レーン番号
211
+ */
212
+ g_customJsObj.dummyFrz.push((_j) => {
213
+
214
+ });
215
+
216
+ /**
217
+ * Appearanceフィルター動作時
218
+ * @param {number} _topNum 上部のフィルターに対応するmainSpriteの番号
219
+ * @param {number} _bottomNum 下部のフィルターに対応するmainSpriteの番号
158
220
  */
159
- g_customJsObj.dummyFrz.push(() => {
221
+ g_customJsObj.appearanceFilter.push((_topNum, _bottomNum) => {
160
222
 
161
223
  });
162
224
 
163
225
  /**
164
226
  * 判定カスタム処理 (引数は共通で1つ保持)
165
227
  * @param {number} _difFrame タイミング誤差(フレーム数)
228
+ * @param {number} _j レーン番号
166
229
  */
167
230
  // イイ
168
- g_customJsObj.judg_ii.push((_difFrame) => {
231
+ g_customJsObj.judg_ii.push((_difFrame, _j) => {
169
232
 
170
233
  });
171
234
 
172
235
  // シャキン
173
- g_customJsObj.judg_shakin.push((_difFrame) => {
236
+ g_customJsObj.judg_shakin.push((_difFrame, _j) => {
174
237
 
175
238
  });
176
239
 
177
240
  // マターリ
178
- g_customJsObj.judg_matari.push((_difFrame) => {
241
+ g_customJsObj.judg_matari.push((_difFrame, _j) => {
179
242
 
180
243
  });
181
244
 
182
245
  // ショボーン
183
- g_customJsObj.judg_shobon.push((_difFrame) => {
246
+ g_customJsObj.judg_shobon.push((_difFrame, _j) => {
184
247
 
185
248
  });
186
249
 
187
250
  // ウワァン
188
- g_customJsObj.judg_uwan.push((_difFrame) => {
251
+ g_customJsObj.judg_uwan.push((_difFrame, _j) => {
189
252
 
190
253
  });
191
254
 
192
255
  // キター
193
- g_customJsObj.judg_kita.push((_difFrame) => {
256
+ g_customJsObj.judg_kita.push((_difFrame, _j) => {
194
257
 
195
258
  });
196
259
 
197
260
  // イクナイ
198
- g_customJsObj.judg_iknai.push((_difFrame) => {
261
+ g_customJsObj.judg_iknai.push((_difFrame, _j) => {
262
+
263
+ });
264
+
265
+ // Excessive
266
+ g_customJsObj.judg_excessive.push((_difFrame, _j) => {
199
267
 
200
268
  });
201
269
 
202
270
  // 通常フリーズアローヒット時
203
- g_customJsObj.judg_frzHit.push((_difFrame) => {
271
+ g_customJsObj.judg_frzHit.push((_difFrame, _j) => {
204
272
 
205
273
  });
206
274
 
207
275
  // ダミーフリーズアローヒット時
208
- g_customJsObj.judg_dummyFrzHit.push((_difFrame) => {
276
+ g_customJsObj.judg_dummyFrzHit.push((_difFrame, _j) => {
209
277
 
210
278
  });
211
279
 
212
280
  /**
213
281
  * メイン画面(フレーム毎表示) [Scene: Main / Banana]
214
282
  * - 現在のフレーム数は g_scoreObj.baseFrame で取得可能
283
+ * - 矢印生成、移動前に処理を行いたいときに記述
215
284
  */
216
285
  g_customJsObj.mainEnterFrame.push(() => {
217
286
 
218
287
  });
219
288
 
289
+ /**
290
+ * メイン画面(フレーム毎表示、タイマー直前)
291
+ * - 矢印生成、移動後に処理を行いたいときに記述
292
+ * @param {number[]} arrowCnts 矢印カウント情報
293
+ * @param {number[]} frzCnts フリーズアローカウント情報
294
+ * @param {number[]} dummyArrowCnts ダミー矢印カウント情報
295
+ * @param {number[]} dummyFrzCnts ダミーフリーズアローカウント情報
296
+ */
297
+ g_customJsObj.mainBeforeFrameTimer.push(({ arrowCnts, frzCnts, dummyArrowCnts, dummyFrzCnts }) => {
298
+
299
+ });
300
+
220
301
  /**
221
302
  * 結果画面(初期表示) [Scene: Result / Grape]
222
303
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "danoniplus",
3
- "version": "50.2.0",
3
+ "version": "50.3.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",