Source: clickActions.js

  1. "use strict";
  2. // SPDX-License-Identifier: GPL-3.0-or-later
  3. // myMPD (c) 2018-2025 Juergen Mang <mail@jcgames.de>
  4. // https://github.com/jcorporation/mympd
  5. /** @module clickActions_js */
  6. /**
  7. * Handler for quick remove button
  8. * @param {EventTarget} target event target
  9. * @returns {void}
  10. */
  11. function clickQuickRemove(target) {
  12. let dataNode = target.parentNode.parentNode;
  13. if (dataNode.classList.contains('row')) {
  14. dataNode = dataNode.parentNode;
  15. }
  16. switch(app.id) {
  17. case 'QueueCurrent': {
  18. const songId = getData(dataNode, 'songid');
  19. removeFromQueueIDs([songId]);
  20. break;
  21. }
  22. case 'BrowsePlaylistList': {
  23. const plist = getData(dataNode, 'uri');
  24. showDelPlaylist([plist]);
  25. break;
  26. }
  27. case 'BrowsePlaylistDetail': {
  28. const pos = getData(dataNode, 'pos');
  29. const plist = getDataId('BrowsePlaylistDetailList', 'uri');
  30. removeFromPlaylistPositions(plist, [pos]);
  31. break;
  32. }
  33. case 'QueueJukeboxSong':
  34. case 'QueueJukeboxAlbum': {
  35. const pos = getData(dataNode, 'pos');
  36. delQueueJukeboxEntries([pos]);
  37. break;
  38. }
  39. default:
  40. logError('Invalid appid' + app.id);
  41. }
  42. }
  43. /**
  44. * Handler for quick play button
  45. * @param {EventTarget} target event target
  46. * @returns {void}
  47. */
  48. function clickQuickPlay(target) {
  49. let dataNode = target.parentNode.parentNode;
  50. if (dataNode.classList.contains('row')) {
  51. dataNode = dataNode.parentNode;
  52. }
  53. const type = getData(dataNode, 'type');
  54. const uri = [];
  55. switch(type) {
  56. case 'album':
  57. uri.push(getData(dataNode, 'AlbumId'));
  58. break;
  59. case 'disc':
  60. uri.push(getData(dataNode, 'AlbumId'), getData(dataNode, 'Disc'));
  61. break;
  62. case 'work':
  63. uri.push(getData(dataNode, 'AlbumId'), getData(dataNode, 'Work'));
  64. break;
  65. default:
  66. uri.push(getData(dataNode, 'uri'));
  67. }
  68. switch (settings.webuiSettings.clickQuickPlay) {
  69. case 'append': return appendQueue(type, uri);
  70. case 'appendPlay': return appendPlayQueue(type, uri);
  71. case 'insertAfterCurrent': return insertAfterCurrentQueue(type, uri);
  72. case 'insertPlayAfterCurrent': return insertPlayAfterCurrentQueue(type, uri);
  73. case 'replace': return replaceQueue(type, uri);
  74. case 'replacePlay': return replacePlayQueue(type, uri);
  75. default: logError('Invalid action: ' + settings.webuiSettings.clickQuickPlay);
  76. }
  77. }
  78. /**
  79. * Click song handler
  80. * @param {string} uri song uri
  81. * @param {event} event the event
  82. * @returns {void}
  83. */
  84. function clickSong(uri, event) {
  85. switch (settings.webuiSettings.clickSong) {
  86. case 'append': return appendQueue('song', [uri]);
  87. case 'appendPlay': return appendPlayQueue('song', [uri]);
  88. case 'insertAfterCurrent': return insertAfterCurrentQueue('song', [uri]);
  89. case 'insertPlayAfterCurrent': return insertPlayAfterCurrentQueue('song', [uri]);
  90. case 'replace': return replaceQueue('song', [uri]);
  91. case 'replacePlay': return replacePlayQueue('song', [uri]);
  92. case 'view': return songDetails(uri);
  93. case 'context': return showContextMenu(event);
  94. default: logError('Invalid action: ' + settings.webuiSettings.clickSong);
  95. }
  96. }
  97. /**
  98. * Handler for webradioDB links
  99. * @param {string} uri stream uri
  100. * @param {event} event the event
  101. * @returns {void}
  102. */
  103. function clickWebradiodb(uri, event) {
  104. switch (settings.webuiSettings.clickWebradiodb) {
  105. case 'append': return appendQueue('song', [uri]);
  106. case 'appendPlay': return appendPlayQueue('song', [uri]);
  107. case 'insertAfterCurrent': return insertAfterCurrentQueue('song', [uri]);
  108. case 'insertPlayAfterCurrent': return insertPlayAfterCurrentQueue('song', [uri]);
  109. case 'replace': return replaceQueue('song', [uri]);
  110. case 'replacePlay': return replacePlayQueue('song', [uri]);
  111. case 'view': return showWebradiodbDetails(uri);
  112. case 'context': return showContextMenu(event);
  113. default: logError('Invalid action: ' + settings.webuiSettings.clickWebradiodb);
  114. }
  115. }
  116. /**
  117. * Handler for webradio favorites links
  118. * @param {string} uri webradio favorite uri (filename only)
  119. * @param {event} event the event
  120. * @returns {void}
  121. */
  122. function clickRadioFavorites(uri, event) {
  123. switch(settings.webuiSettings.clickRadioFavorites) {
  124. case 'append': return appendQueue('webradio', [uri]);
  125. case 'appendPlay': return appendPlayQueue('webradio', [uri]);
  126. case 'insertAfterCurrent': return insertAfterCurrentQueue('webradio', [uri]);
  127. case 'insertPlayAfterCurrent': return insertPlayAfterCurrentQueue('webradio', [uri]);
  128. case 'replace': return replaceQueue('webradio', [uri]);
  129. case 'replacePlay': return replacePlayQueue('webradio', [uri]);
  130. case 'edit': return editRadioFavorite(uri);
  131. case 'context': return showContextMenu(event);
  132. default: logError('Invalid action: ' + settings.webuiSettings.clickRadioFavorites);
  133. }
  134. }
  135. /**
  136. * Handler for song links in queue
  137. * @param {string} songid the song id
  138. * @param {string} uri the song uri
  139. * @param {event} event the event
  140. * @returns {void}
  141. */
  142. function clickQueueSong(songid, uri, event) {
  143. switch(settings.webuiSettings.clickQueueSong) {
  144. case 'play':
  145. if (songid === null) {
  146. return;
  147. }
  148. if (currentState.currentSongId === songid) {
  149. return clickPlay();
  150. }
  151. return sendAPI("MYMPD_API_PLAYER_PLAY_SONG", {
  152. "songId": songid
  153. }, null, false);
  154. case 'view':
  155. if (uri === null) {
  156. return;
  157. }
  158. return songDetails(uri);
  159. case 'context':
  160. return showContextMenu(event);
  161. default: logError('Invalid action: ' + settings.webuiSettings.clickQueueSong);
  162. }
  163. }
  164. /**
  165. * Handler for playlist links
  166. * @param {string} uri playlist uri
  167. * @param {event} event the event
  168. * @returns {void}
  169. */
  170. function clickPlaylist(uri, event) {
  171. switch(settings.webuiSettings.clickPlaylist) {
  172. case 'append': return appendQueue('plist', [uri]);
  173. case 'appendPlay': return appendPlayQueue('plist', [uri]);
  174. case 'insertAfterCurrent': return insertAfterCurrentQueue('plist', [uri]);
  175. case 'insertPlayAfterCurrent': return insertPlayAfterCurrentQueue('plist', [uri]);
  176. case 'replace': return replaceQueue('plist', [uri]);
  177. case 'replacePlay': return replacePlayQueue('plist', [uri]);
  178. case 'view': return gotoPlaylist(uri);
  179. case 'context': return showContextMenu(event);
  180. default: logError('Invalid action: ' + settings.webuiSettings.clickPlaylist);
  181. }
  182. }
  183. /**
  184. * Handler for click on playlists in filesystem view
  185. * @param {string} uri playlist uri
  186. * @param {event} event the event
  187. * @returns {void}
  188. */
  189. function clickFilesystemPlaylist(uri, event) {
  190. switch(settings.webuiSettings.clickFilesystemPlaylist) {
  191. case 'append': return appendQueue('plist', [uri]);
  192. case 'appendPlay': return appendPlayQueue('plist', [uri]);
  193. case 'insertAfterCurrent': return insertAfterCurrentQueue('plist', [uri]);
  194. case 'insertPlayAfterCurrent': return insertPlayAfterCurrentQueue('plist', [uri]);
  195. case 'replace': return replaceQueue('plist', [uri]);
  196. case 'replacePlay': return replacePlayQueue('plist', [uri]);
  197. case 'view':
  198. //remember offset for current browse uri
  199. browseFilesystemHistory[app.current.filter] = {
  200. "offset": app.current.offset,
  201. "scrollPos": document.body.scrollTop ? document.body.scrollTop : document.documentElement.scrollTop
  202. };
  203. //reset search and show playlist
  204. app.current.search = '';
  205. appGoto('Browse', 'Filesystem', undefined, 0, app.current.limit, uri, app.current.sort, 'plist', '', 0);
  206. break;
  207. case 'context': return showContextMenu(event);
  208. default: logError('Invalid action: ' + settings.webuiSettings.clickFilesystemPlaylist);
  209. }
  210. }
  211. /**
  212. * Handler for click on folder in filesystem view
  213. * @param {string} uri folder uri
  214. * @returns {void}
  215. */
  216. function clickFolder(uri) {
  217. //remember offset for current browse uri
  218. browseFilesystemHistory[app.current.filter] = {
  219. "offset": app.current.offset,
  220. "scrollPos": getScrollPosY()
  221. };
  222. //reset search and open folder
  223. app.current.search = '';
  224. appGoto('Browse', 'Filesystem', undefined, 0, app.current.limit, uri, app.current.sort, 'dir', '', 0);
  225. }
  226. /**
  227. * Seeks the current song forward by 5s
  228. * @returns {void}
  229. */
  230. function seekRelativeForward() {
  231. seekRelative(5);
  232. }
  233. /**
  234. * Seeks the current song backward by 5s
  235. * @returns {void}
  236. */
  237. function seekRelativeBackward() {
  238. seekRelative(-5);
  239. }
  240. /**
  241. * Seeks the current song by offset seconds
  242. * @param {number} offset relative seek offset
  243. * @returns {void}
  244. */
  245. function seekRelative(offset) {
  246. sendAPI("MYMPD_API_PLAYER_SEEK_CURRENT", {
  247. "seek": offset,
  248. "relative": true
  249. }, null, false);
  250. }
  251. /**
  252. * Handler for click on play button
  253. * @returns {void}
  254. */
  255. //eslint-disable-next-line no-unused-vars
  256. function clickPlay() {
  257. switch(currentState.state) {
  258. case 'play':
  259. if (settings.webuiSettings.footerPlaybackControls === 'stop') {
  260. sendAPI("MYMPD_API_PLAYER_STOP", {}, null, false);
  261. }
  262. else {
  263. sendAPI("MYMPD_API_PLAYER_PAUSE", {}, null, false);
  264. }
  265. break;
  266. case 'pause':
  267. sendAPI("MYMPD_API_PLAYER_RESUME", {}, null, false);
  268. break;
  269. default:
  270. //fallback if playstate is stop or unknown
  271. sendAPI("MYMPD_API_PLAYER_PLAY", {}, null, false);
  272. }
  273. }
  274. /**
  275. * Handler for click on stop button
  276. * @returns {void}
  277. */
  278. //eslint-disable-next-line no-unused-vars
  279. function clickStop() {
  280. sendAPI("MYMPD_API_PLAYER_STOP", {}, null, false);
  281. }
  282. /**
  283. * Handler for click on prev song button
  284. * @returns {void}
  285. */
  286. //eslint-disable-next-line no-unused-vars
  287. function clickPrev() {
  288. sendAPI("MYMPD_API_PLAYER_PREV", {}, null, false);
  289. }
  290. /**
  291. * Handler for click on next song button
  292. * @returns {void}
  293. */
  294. //eslint-disable-next-line no-unused-vars
  295. function clickNext() {
  296. sendAPI("MYMPD_API_PLAYER_NEXT", {}, null, false);
  297. }
  298. /**
  299. * Handler for click on fast rewind button
  300. * @returns {void}
  301. */
  302. //eslint-disable-next-line no-unused-vars
  303. function clickFastRewind() {
  304. clickSeek(-settings.webuiSettings.seekStep, true);
  305. }
  306. /**
  307. * Handler for click on fast rewind button in playback controls popover
  308. * @returns {void}
  309. */
  310. //eslint-disable-next-line no-unused-vars
  311. function clickFastRewindValue() {
  312. lastSeekStep = parseToSeconds(elGetById('popoverFooterSeekInput').value);
  313. clickSeek(-lastSeekStep, true);
  314. }
  315. /**
  316. * Handler for click on fast rewind button
  317. * @returns {void}
  318. */
  319. //eslint-disable-next-line no-unused-vars
  320. function clickFastForward() {
  321. clickSeek(settings.webuiSettings.seekStep, true);
  322. }
  323. /**
  324. * Handler for click on fast rewind button in playback controls popover
  325. * @returns {void}
  326. */
  327. //eslint-disable-next-line no-unused-vars
  328. function clickFastForwardValue() {
  329. lastSeekStep = parseToSeconds(elGetById('popoverFooterSeekInput').value);
  330. clickSeek(lastSeekStep, true);
  331. }
  332. /**
  333. * Handler for click on goto position button in playback controls popover
  334. * @returns {void}
  335. */
  336. //eslint-disable-next-line no-unused-vars
  337. function clickGotoPos() {
  338. const seekToPos = parseToSeconds(elGetById('popoverFooterGotoInput').value);
  339. clickSeek(seekToPos, false);
  340. }
  341. /**
  342. * Shows the advanced playback control popover
  343. * @param {Event} event triggering event
  344. * @returns {void}
  345. */
  346. function toggleAdvPlaycontrolsPopover(event) {
  347. if (event.target.closest('.dropdown-menu') !== null) {
  348. return;
  349. }
  350. event.preventDefault();
  351. event.stopPropagation();
  352. if (domCache.footer.getAttribute('aria-describedby') === null) {
  353. showPopover(domCache.footer, 'footer');
  354. }
  355. else {
  356. hidePopover();
  357. }
  358. }
  359. /**
  360. * Seek handler
  361. * @param {number} value seek by/to value
  362. * @param {boolean} relative true = number is relative
  363. * @returns {void}
  364. */
  365. function clickSeek(value, relative) {
  366. sendAPI("MYMPD_API_PLAYER_SEEK_CURRENT", {
  367. "seek": value,
  368. "relative": relative
  369. }, null, false);
  370. }
  371. /**
  372. * Handler for click on single button
  373. * @param {string} mode single mode: "0" = off, "1" = single, "oneshot" = single one shot
  374. * @returns {void}
  375. */
  376. //eslint-disable-next-line no-unused-vars
  377. function clickSingle(mode) {
  378. sendAPI("MYMPD_API_PLAYER_OPTIONS_SET", {
  379. "single": mode
  380. }, null, false);
  381. }
  382. /**
  383. * Handler for click on consume button
  384. * @param {string} mode single mode: "0" = off, "1" = consume, "oneshot" = consume one shot
  385. * @returns {void}
  386. */
  387. //eslint-disable-next-line no-unused-vars
  388. function clickConsume(mode) {
  389. sendAPI("MYMPD_API_PLAYER_OPTIONS_SET", {
  390. "consume": mode
  391. }, null, false);
  392. }
  393. /**
  394. * Handler for resume song dropdown actions
  395. * @param {Event} event Click event
  396. * @returns {void}
  397. */
  398. function clickResumeSong(event) {
  399. event.preventDefault();
  400. if (event.target.nodeName !== 'BUTTON') {
  401. return;
  402. }
  403. const dataNode = event.target.closest('.btn-group');
  404. const uri = getData(dataNode, 'uri');
  405. const action = event.target.getAttribute('data-action');
  406. resumeSong(uri, action);
  407. }
  408. /**
  409. * Handler for song resume - click on resume indicator
  410. * @param {EventTarget} target event target
  411. * @returns {void}
  412. */
  413. function clickQuickResumeSong(target) {
  414. const uri = getData(target, 'uri');
  415. resumeSong(uri, settings.webuiSettings.clickQuickPlay);
  416. }
  417. /**
  418. * Handler for resume playlist dropdown actions
  419. * @param {Event} event Click event
  420. * @returns {void}
  421. */
  422. function clickResumePlist(event) {
  423. event.preventDefault();
  424. if (event.target.nodeName !== 'BUTTON') {
  425. return;
  426. }
  427. const dataNode = event.target.closest('.btn-group');
  428. const pos = getData(dataNode, 'pos');
  429. const uri = getDataId('BrowsePlaylistDetailList', 'uri');
  430. const action = event.target.getAttribute('data-action');
  431. resumePlist(uri, pos, action);
  432. }
  433. /**
  434. * Handler for resume album dropdown actions
  435. * @param {Event} event Click event
  436. * @returns {void}
  437. */
  438. function clickResumeAlbum(event) {
  439. event.preventDefault();
  440. if (event.target.nodeName !== 'BUTTON') {
  441. return;
  442. }
  443. const dataNode = event.target.closest('.btn-group');
  444. const pos = getData(dataNode, 'pos');
  445. const albumId = getDataId('viewDatabaseAlbumDetailCover', 'AlbumId');
  446. const action = event.target.getAttribute('data-action');
  447. resumeAlbum(albumId, pos, action);
  448. }