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 += '
'; var moreIcon = "more_horiz"; 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().show({ items: items, serverId: serverId }); }); hideSelections(); dispatchNeedsRefresh(); break; case "playlist": require(["playlistEditor"], function (playlistEditor) { new playlistEditor().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 }); }; }; });