define(['browser', 'appStorage', 'apphost', 'loading', 'connectionManager', 'globalize', 'appRouter', 'dom', 'css!./multiSelect'], function (browser, appStorage, appHost, loading, connectionManager, globalize, appRouter, dom) { 'use strict'; var selectedItems = []; var selectedElements = []; var currentSelectionCommandsPanel; function hideSelections() { var selectionCommandsPanel = currentSelectionCommandsPanel; if (selectionCommandsPanel) { selectionCommandsPanel.parentNode.removeChild(selectionCommandsPanel); currentSelectionCommandsPanel = null; selectedItems = []; selectedElements = []; var elems = document.querySelectorAll('.itemSelectionPanel'); for (var i = 0, length = elems.length; i < length; i++) { var parent = elems[i].parentNode; parent.removeChild(elems[i]); parent.classList.remove('withMultiSelect'); } } } function onItemSelectionPanelClick(e, itemSelectionPanel) { // toggle the checkbox, if it wasn't clicked on if (!dom.parentWithClass(e.target, 'chkItemSelect')) { var chkItemSelect = itemSelectionPanel.querySelector('.chkItemSelect'); if (chkItemSelect) { if (chkItemSelect.classList.contains('checkedInitial')) { chkItemSelect.classList.remove('checkedInitial'); } else { var newValue = !chkItemSelect.checked; chkItemSelect.checked = newValue; updateItemSelection(chkItemSelect, newValue); } } } e.preventDefault(); e.stopPropagation(); return false; } function updateItemSelection(chkItemSelect, selected) { var id = dom.parentWithAttribute(chkItemSelect, 'data-id').getAttribute('data-id'); if (selected) { var current = selectedItems.filter(function (i) { return i === id; }); if (!current.length) { selectedItems.push(id); selectedElements.push(chkItemSelect); } } else { selectedItems = selectedItems.filter(function (i) { return i !== id; }); selectedElements = selectedElements.filter(function (i) { return i !== chkItemSelect; }); } if (selectedItems.length) { var itemSelectionCount = document.querySelector('.itemSelectionCount'); if (itemSelectionCount) { itemSelectionCount.innerHTML = selectedItems.length; } } else { hideSelections(); } } function onSelectionChange(e) { updateItemSelection(this, this.checked); } function showSelection(item, isChecked) { var itemSelectionPanel = item.querySelector('.itemSelectionPanel'); if (!itemSelectionPanel) { itemSelectionPanel = document.createElement('div'); itemSelectionPanel.classList.add('itemSelectionPanel'); var parent = item.querySelector('.cardBox') || item.querySelector('.cardContent'); parent.classList.add('withMultiSelect'); parent.appendChild(itemSelectionPanel); var cssClass = 'chkItemSelect'; if (isChecked && !browser.firefox) { // In firefox, the initial tap hold doesnt' get treated as a click // In other browsers it does, so we need to make sure that initial click is ignored cssClass += ' checkedInitial'; } var checkedAttribute = isChecked ? ' checked' : ''; itemSelectionPanel.innerHTML = ''; var chkItemSelect = itemSelectionPanel.querySelector('.chkItemSelect'); chkItemSelect.addEventListener('change', onSelectionChange); } } function showSelectionCommands() { var selectionCommandsPanel = currentSelectionCommandsPanel; if (!selectionCommandsPanel) { selectionCommandsPanel = document.createElement('div'); selectionCommandsPanel.classList.add('selectionCommandsPanel'); document.body.appendChild(selectionCommandsPanel); currentSelectionCommandsPanel = selectionCommandsPanel; var html = ''; html += ''; html += '

'; const moreIcon = 'more_vert'; html += ''; selectionCommandsPanel.innerHTML = html; selectionCommandsPanel.querySelector('.btnCloseSelectionPanel').addEventListener('click', hideSelections); var btnSelectionPanelOptions = selectionCommandsPanel.querySelector('.btnSelectionPanelOptions'); dom.addEventListener(btnSelectionPanelOptions, 'click', showMenuForSelectedItems, { passive: true }); } } function alertText(options) { return new Promise(function (resolve, reject) { require(['alert'], function (alert) { alert(options).then(resolve, resolve); }); }); } function deleteItems(apiClient, itemIds) { return new Promise(function (resolve, reject) { var msg = globalize.translate('ConfirmDeleteItem'); var title = globalize.translate('HeaderDeleteItem'); if (itemIds.length > 1) { msg = globalize.translate('ConfirmDeleteItems'); title = globalize.translate('HeaderDeleteItems'); } require(['confirm'], function (confirm) { confirm(msg, title).then(function () { var promises = itemIds.map(function (itemId) { apiClient.deleteItem(itemId); }); Promise.all(promises).then(resolve, function () { alertText(globalize.translate('ErrorDeletingItem')).then(reject, reject); }); }, reject); }); }); } function showMenuForSelectedItems(e) { var apiClient = connectionManager.currentApiClient(); apiClient.getCurrentUser().then(function (user) { var menuItems = []; menuItems.push({ name: globalize.translate('AddToCollection'), id: 'addtocollection', icon: 'add' }); menuItems.push({ name: globalize.translate('AddToPlaylist'), id: 'playlist', icon: 'playlist_add' }); // TODO: Be more dynamic based on what is selected if (user.Policy.EnableContentDeletion) { menuItems.push({ name: globalize.translate('Delete'), id: 'delete', icon: 'delete' }); } if (user.Policy.EnableContentDownloading && appHost.supports('filedownload')) { menuItems.push({ name: globalize.translate('ButtonDownload'), id: 'download', icon: 'file_download' }); } if (user.Policy.IsAdministrator) { menuItems.push({ name: globalize.translate('GroupVersions'), id: 'groupvideos', icon: 'call_merge' }); } menuItems.push({ name: globalize.translate('MarkPlayed'), id: 'markplayed', icon: 'check_box' }); menuItems.push({ name: globalize.translate('MarkUnplayed'), id: 'markunplayed', icon: 'check_box_outline_blank' }); menuItems.push({ name: globalize.translate('RefreshMetadata'), id: 'refresh', icon: 'refresh' }); require(['actionsheet'], function (actionsheet) { actionsheet.show({ items: menuItems, positionTo: e.target, callback: function (id) { var items = selectedItems.slice(0); var serverId = apiClient.serverInfo().Id; switch (id) { case 'addtocollection': require(['collectionEditor'], function (collectionEditor) { new collectionEditor.default().show({ items: items, serverId: serverId }); }); hideSelections(); dispatchNeedsRefresh(); break; case 'playlist': require(['playlistEditor'], function (playlistEditor) { new playlistEditor.default().show({ items: items, serverId: serverId }); }); hideSelections(); dispatchNeedsRefresh(); break; case 'delete': deleteItems(apiClient, items).then(dispatchNeedsRefresh); hideSelections(); dispatchNeedsRefresh(); break; case 'groupvideos': combineVersions(apiClient, items); break; case 'markplayed': items.forEach(function (itemId) { apiClient.markPlayed(apiClient.getCurrentUserId(), itemId); }); hideSelections(); dispatchNeedsRefresh(); break; case 'markunplayed': items.forEach(function (itemId) { apiClient.markUnplayed(apiClient.getCurrentUserId(), itemId); }); hideSelections(); dispatchNeedsRefresh(); break; case 'refresh': require(['refreshDialog'], function (refreshDialog) { new refreshDialog({ itemIds: items, serverId: serverId }).show(); }); hideSelections(); dispatchNeedsRefresh(); break; default: break; } } }); }); }); } function dispatchNeedsRefresh() { var elems = []; [].forEach.call(selectedElements, function (i) { var container = dom.parentWithAttribute(i, 'is', 'emby-itemscontainer'); if (container && elems.indexOf(container) === -1) { elems.push(container); } }); for (var i = 0, length = elems.length; i < length; i++) { elems[i].notifyRefreshNeeded(true); } } function combineVersions(apiClient, selection) { if (selection.length < 2) { require(['alert'], function (alert) { alert({ text: globalize.translate('PleaseSelectTwoItems') }); }); return; } loading.show(); apiClient.ajax({ type: 'POST', url: apiClient.getUrl('Videos/MergeVersions', { Ids: selection.join(',') }) }).then(function () { loading.hide(); hideSelections(); dispatchNeedsRefresh(); }); } function showSelections(initialCard) { require(['emby-checkbox'], function () { var cards = document.querySelectorAll('.card'); for (var i = 0, length = cards.length; i < length; i++) { showSelection(cards[i], initialCard === cards[i]); } showSelectionCommands(); updateItemSelection(initialCard, true); }); } function onContainerClick(e) { var target = e.target; if (selectedItems.length) { var card = dom.parentWithClass(target, 'card'); if (card) { var itemSelectionPanel = card.querySelector('.itemSelectionPanel'); if (itemSelectionPanel) { return onItemSelectionPanelClick(e, itemSelectionPanel); } } e.preventDefault(); e.stopPropagation(); return false; } } document.addEventListener('viewbeforehide', hideSelections); return function (options) { var self = this; var container = options.container; function onTapHold(e) { var card = dom.parentWithClass(e.target, 'card'); if (card) { showSelections(card); } e.preventDefault(); // It won't have this if it's a hammer event if (e.stopPropagation) { e.stopPropagation(); } return false; } function getTouches(e) { return e.changedTouches || e.targetTouches || e.touches; } var touchTarget; var touchStartTimeout; var touchStartX; var touchStartY; function onTouchStart(e) { var touch = getTouches(e)[0]; touchTarget = null; touchStartX = 0; touchStartY = 0; if (touch) { touchStartX = touch.clientX; touchStartY = touch.clientY; var element = touch.target; if (element) { var card = dom.parentWithClass(element, 'card'); if (card) { if (touchStartTimeout) { clearTimeout(touchStartTimeout); touchStartTimeout = null; } touchTarget = card; touchStartTimeout = setTimeout(onTouchStartTimerFired, 550); } } } } function onTouchMove(e) { if (touchTarget) { var touch = getTouches(e)[0]; var deltaX; var deltaY; if (touch) { var touchEndX = touch.clientX || 0; var touchEndY = touch.clientY || 0; deltaX = Math.abs(touchEndX - (touchStartX || 0)); deltaY = Math.abs(touchEndY - (touchStartY || 0)); } else { deltaX = 100; deltaY = 100; } if (deltaX >= 5 || deltaY >= 5) { onMouseOut(e); } } } function onTouchEnd(e) { onMouseOut(e); } function onMouseDown(e) { if (touchStartTimeout) { clearTimeout(touchStartTimeout); touchStartTimeout = null; } touchTarget = e.target; touchStartTimeout = setTimeout(onTouchStartTimerFired, 550); } function onMouseOut(e) { if (touchStartTimeout) { clearTimeout(touchStartTimeout); touchStartTimeout = null; } touchTarget = null; } function onTouchStartTimerFired() { if (!touchTarget) { return; } var card = dom.parentWithClass(touchTarget, 'card'); touchTarget = null; if (card) { showSelections(card); } } function initTapHold(element) { // mobile safari doesn't allow contextmenu override if (browser.touch && !browser.safari) { element.addEventListener('contextmenu', onTapHold); } else { dom.addEventListener(element, 'touchstart', onTouchStart, { passive: true }); dom.addEventListener(element, 'touchmove', onTouchMove, { passive: true }); dom.addEventListener(element, 'touchend', onTouchEnd, { passive: true }); dom.addEventListener(element, 'touchcancel', onTouchEnd, { passive: true }); dom.addEventListener(element, 'mousedown', onMouseDown, { passive: true }); dom.addEventListener(element, 'mouseleave', onMouseOut, { passive: true }); dom.addEventListener(element, 'mouseup', onMouseOut, { passive: true }); } } initTapHold(container); if (options.bindOnClick !== false) { container.addEventListener('click', onContainerClick); } self.onContainerClick = onContainerClick; self.destroy = function () { container.removeEventListener('click', onContainerClick); container.removeEventListener('contextmenu', onTapHold); var element = container; dom.removeEventListener(element, 'touchstart', onTouchStart, { passive: true }); dom.removeEventListener(element, 'touchmove', onTouchMove, { passive: true }); dom.removeEventListener(element, 'touchend', onTouchEnd, { passive: true }); // this fires in safari due to magnifying class //dom.removeEventListener(element, "touchcancel", onTouchEnd, { // passive: true //}); dom.removeEventListener(element, 'mousedown', onMouseDown, { passive: true }); dom.removeEventListener(element, 'mouseleave', onMouseOut, { passive: true }); dom.removeEventListener(element, 'mouseup', onMouseOut, { passive: true }); }; }; });