diff --git a/dashboard-ui/bower_components/emby-webcomponents/.bower.json b/dashboard-ui/bower_components/emby-webcomponents/.bower.json index 3f1d60e7fb..56d6368223 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/.bower.json +++ b/dashboard-ui/bower_components/emby-webcomponents/.bower.json @@ -15,12 +15,12 @@ }, "devDependencies": {}, "ignore": [], - "version": "1.4.96", - "_release": "1.4.96", + "version": "1.4.97", + "_release": "1.4.97", "_resolution": { "type": "version", - "tag": "1.4.96", - "commit": "4c9f9b8c4937da15ac8c81d554e768f0ef6e944e" + "tag": "1.4.97", + "commit": "8b11aa9b8f073cbcd9eacf3170efce38d43a5526" }, "_source": "https://github.com/MediaBrowser/emby-webcomponents.git", "_target": "^1.2.0", diff --git a/dashboard-ui/bower_components/emby-webcomponents/dialoghelper/dialoghelper.js b/dashboard-ui/bower_components/emby-webcomponents/dialoghelper/dialoghelper.js index f69eccea45..2a1570d06e 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/dialoghelper/dialoghelper.js +++ b/dashboard-ui/bower_components/emby-webcomponents/dialoghelper/dialoghelper.js @@ -166,11 +166,7 @@ // Without this, seeing some script errors in Firefox // Also for some reason it won't auto-focus without a delay here, still investigating that - var delay = enableAnimation() ? 300 : 300; - - setTimeout(function () { - focusManager.autoFocus(dlg); - }, delay); + focusManager.autoFocus(dlg); } function safeBlur(el) { diff --git a/dashboard-ui/bower_components/emby-webcomponents/emby-itemscontainer/emby-itemscontainer.js b/dashboard-ui/bower_components/emby-webcomponents/emby-itemscontainer/emby-itemscontainer.js index 08e5393958..a1703653ae 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/emby-itemscontainer/emby-itemscontainer.js +++ b/dashboard-ui/bower_components/emby-webcomponents/emby-itemscontainer/emby-itemscontainer.js @@ -36,6 +36,7 @@ var target = e.target; var card = parentWithAttribute(target, 'data-id'); + if (card) { //var itemSelectionPanel = card.querySelector('.itemSelectionPanel'); @@ -49,11 +50,11 @@ positionTo: target, itemsContainer: itemsContainer }); - } - e.preventDefault(); - e.stopPropagation(); - return false; + e.preventDefault(); + e.stopPropagation(); + return false; + } } function getShortcutOptions() { @@ -82,13 +83,32 @@ }); }; + ItemsContainerProtoType.enableMultiSelect = function (enabled) { + + var current = this.multiSelect; + + if (!enabled && current) { + current.destroy(); + this.multiSelect = null; + return; + } + + if (current) { + return; + } + + var self = this; + require(['multiSelect'], function (MultiSelect) { + self.multiSelect = new MultiSelect(self); + }); + }; + ItemsContainerProtoType.attachedCallback = function () { + this.addEventListener('click', onClick); - // mobile safari doesn't allow contextmenu override - if (browser.safari && browser.mobile) { + if (browser.mobile) { this.addEventListener('contextmenu', disableEvent); - // todo: use tap hold } else { this.addEventListener('contextmenu', onContextMenu); } @@ -97,10 +117,17 @@ this.enableHoverMenu(true); } + if (layoutManager.desktop || layoutManager.mobile) { + this.enableMultiSelect(true); + } + itemShortcuts.on(this, getShortcutOptions()); }; ItemsContainerProtoType.detachedCallback = function () { + + this.enableHoverMenu(false); + this.enableMultiSelect(false); this.removeEventListener('click', onClick); this.removeEventListener('contextmenu', onContextMenu); this.removeEventListener('contextmenu', disableEvent); diff --git a/dashboard-ui/bower_components/emby-webcomponents/itemhovermenu/itemhovermenu.css b/dashboard-ui/bower_components/emby-webcomponents/itemhovermenu/itemhovermenu.css index 656ea2327e..3786543ce8 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/itemhovermenu/itemhovermenu.css +++ b/dashboard-ui/bower_components/emby-webcomponents/itemhovermenu/itemhovermenu.css @@ -39,10 +39,16 @@ } .cardOverlayButtons .fab { - background-color: #333; + background-color: #282828; margin-right: .25em; } + .cardOverlayButtons .fab i.md-icon { + width: auto !important; + height: auto !important; + font-size: 20px !important; + } + @media all and (max-width: 1200px) { .cardOverlayInner { diff --git a/dashboard-ui/bower_components/emby-webcomponents/itemhovermenu/itemhovermenu.js b/dashboard-ui/bower_components/emby-webcomponents/itemhovermenu/itemhovermenu.js index 4b0ffa31fc..603642ed24 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/itemhovermenu/itemhovermenu.js +++ b/dashboard-ui/bower_components/emby-webcomponents/itemhovermenu/itemhovermenu.js @@ -2,7 +2,6 @@ var preventHover = false; var showOverlayTimeout; - var hoveringElement; function parentWithAttribute(elem, name) { @@ -21,10 +20,6 @@ var elem = e.target; - if (elem != hoveringElement) { - return; - } - if (showOverlayTimeout) { clearTimeout(showOverlayTimeout); showOverlayTimeout = null; @@ -244,7 +239,6 @@ } elem = parentWithAttribute(elem, 'data-id'); - hoveringElement = elem; showOverlayTimeout = setTimeout(function () { onShowTimerExpired(elem); diff --git a/dashboard-ui/bower_components/emby-webcomponents/multiselect/multiselect.css b/dashboard-ui/bower_components/emby-webcomponents/multiselect/multiselect.css new file mode 100644 index 0000000000..63acb6d5e6 --- /dev/null +++ b/dashboard-ui/bower_components/emby-webcomponents/multiselect/multiselect.css @@ -0,0 +1,37 @@ +.itemSelectionPanel { + position: absolute; + bottom: 0; + left: 0; + right: 0; + top: 0; + background-color: rgba(0, 0, 0, .3); + z-index: 999; + border: 1px solid #43A047; +} + +.selectionCommandsPanel { + position: fixed; + top: 0; + left: 0; + right: 0; + height: 50px; + background: #43A047; + z-index: 1000; + padding: 0 .75em 0 .25em; + display: flex; + align-items: center; + color: #fff; +} + +.itemSelectionCount { + font-size: 28px; + vertical-align: middle; + color: #fff; + display: inline-block; + padding-top: 1px; +} + +.itemSelectionPanel .checkboxOutline { + top: 0 !important; + border-radius: 0 !important; +} diff --git a/dashboard-ui/bower_components/emby-webcomponents/multiselect/multiselect.js b/dashboard-ui/bower_components/emby-webcomponents/multiselect/multiselect.js new file mode 100644 index 0000000000..2c1c056178 --- /dev/null +++ b/dashboard-ui/bower_components/emby-webcomponents/multiselect/multiselect.js @@ -0,0 +1,547 @@ +define(['browser', 'apphost', 'loading', 'connectionManager', 'globalize', 'embyRouter', 'css!./multiselect'], function (browser, appHost, loading, connectionManager, globalize, embyRouter) { + + var selectedItems = []; + var selectedElements = []; + var currentSelectionCommandsPanel; + + function parentWithClass(elem, className) { + + while (!elem.classList || !elem.classList.contains(className)) { + elem = elem.parentNode; + + if (!elem) { + return null; + } + } + + return elem; + } + + 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++) { + elems[i].parentNode.removeChild(elems[i]); + } + } + } + + function showTapHoldHelp(element) { + + return; + var page = parentWithClass(element, 'page'); + + if (!page) { + return; + } + + // Don't do this on the home page + if (page.classList.contains('homePage') || page.classList.contains('itemDetailPage') || page.classList.contains('liveTvPage')) { + return; + } + + var expectedValue = "8"; + if (appStorage.getItem("tapholdhelp") == expectedValue) { + return; + } + + appStorage.setItem("tapholdhelp", expectedValue); + + Dashboard.alert({ + message: globalize.translate('TryMultiSelectMessage'), + title: globalize.translate('HeaderTryMultiSelect') + }); + } + + function onItemSelectionPanelClick(e, itemSelectionPanel) { + + // toggle the checkbox, if it wasn't clicked on + if (!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 = parentWithClass(chkItemSelect, 'card').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'); + + item.querySelector('.cardContent').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 shake(elem, iterations) { + var keyframes = [ + { transform: 'translate3d(0, 0, 0)', offset: 0 }, + { transform: 'translate3d(-10px, 0, 0)', offset: 0.1 }, + { transform: 'translate3d(10px, 0, 0)', offset: 0.2 }, + { transform: 'translate3d(-10px, 0, 0)', offset: 0.3 }, + { transform: 'translate3d(10px, 0, 0)', offset: 0.4 }, + { transform: 'translate3d(-10px, 0, 0)', offset: 0.5 }, + { transform: 'translate3d(10px, 0, 0)', offset: 0.6 }, + { transform: 'translate3d(-10px, 0, 0)', offset: 0.7 }, + { transform: 'translate3d(10px, 0, 0)', offset: 0.8 }, + { transform: 'translate3d(-10px, 0, 0)', offset: 0.9 }, + { transform: 'translate3d(0, 0, 0)', offset: 1 }]; + var timing = { duration: 900, iterations: iterations }; + + if (elem.animate) { + elem.animate(keyframes, timing); + } + } + + 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 += ''; + html += ''; + html += '
'; + + html += ''; + + selectionCommandsPanel.innerHTML = html; + + selectionCommandsPanel.querySelector('.btnCloseSelectionPanel').addEventListener('click', hideSelections); + + var btnSelectionPanelOptions = selectionCommandsPanel.querySelector('.btnSelectionPanelOptions'); + + btnSelectionPanelOptions.addEventListener('click', showMenuForSelectedItems); + + if (!browser.mobile) { + shake(btnSelectionPanelOptions, 1); + } + } + } + + 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); + }); + + resolve(); + }, reject); + + }); + }); + } + + function showMenuForSelectedItems(e) { + + var apiClient = connectionManager.currentApiClient(); + + apiClient.getCurrentUser().then(function (user) { + + var menuItems = []; + + menuItems.push({ + name: globalize.translate('sharedcomponents#AddToCollection'), + id: 'addtocollection', + ironIcon: 'add' + }); + + menuItems.push({ + name: globalize.translate('sharedcomponents#AddToPlaylist'), + id: 'playlist', + ironIcon: 'playlist-add' + }); + + if (user.Policy.EnableContentDeletion) { + menuItems.push({ + name: globalize.translate('sharedcomponents#Delete'), + id: 'delete', + ironIcon: 'delete' + }); + } + + if (user.Policy.EnableContentDownloading && appHost.supports('filedownload')) { + //items.push({ + // name: Globalize.translate('ButtonDownload'), + // id: 'download', + // ironIcon: 'file-download' + //}); + } + + menuItems.push({ + name: globalize.translate('sharedcomponents#GroupVersions'), + id: 'groupvideos', + ironIcon: 'call-merge' + }); + + menuItems.push({ + name: globalize.translate('sharedcomponents#MarkPlayed'), + id: 'markplayed' + }); + + menuItems.push({ + name: globalize.translate('sharedcomponents#MarkUnplayed'), + id: 'markunplayed' + }); + + menuItems.push({ + name: globalize.translate('sharedcomponents#Refresh'), + id: 'refresh', + ironIcon: 'refresh' + }); + + menuItems.push({ + name: globalize.translate('sharedcomponents#Sync'), + id: 'sync', + ironIcon: 'sync' + }); + dispatchNeedsRefresh(); + 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(); + break; + case 'playlist': + require(['playlistEditor'], function (playlistEditor) { + new playlistEditor().show({ + items: items, + serverId: serverId + }); + }); + hideSelections(); + break; + case 'delete': + deleteItems(items).then(function () { + embyRouter.goHome(); + }); + hideSelections(); + break; + case 'groupvideos': + combineVersions(apiClient, items); + break; + case 'markplayed': + items.forEach(function (itemId) { + apiClient.markPlayed(apiClient.getCurrentUserId(), itemId); + }); + hideSelections(); + break; + case 'markunplayed': + items.forEach(function (itemId) { + apiClient.markUnplayed(apiClient.getCurrentUserId(), itemId); + }); + hideSelections(); + break; + case 'refresh': + require(['refreshDialog'], function (refreshDialog) { + new refreshDialog({ + itemIds: items, + serverId: serverId + }).show(); + }); + hideSelections(); + break; + case 'sync': + require(['syncDialog'], function (syncDialog) { + syncDialog.showMenu({ + items: items.map(function (i) { + return { + Id: i + }; + }) + }); + }); + hideSelections(); + break; + default: + break; + } + } + }); + + }); + }); + } + + function parentWithAttribute(elem, name, value) { + + while ((value ? elem.getAttribute(name) != value : !elem.getAttribute(name))) { + elem = elem.parentNode; + + if (!elem || !elem.getAttribute) { + return null; + } + } + + return elem; + } + + function dispatchNeedsRefresh() { + + var elems = []; + + [].forEach.call(selectedElements, function (i) { + + var container = 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].dispatchEvent(new CustomEvent('needsrefresh', { + detail: {}, + cancelable: false, + bubbles: true + })); + } + } + + function combineVersions(apiClient, selection) { + + if (selection.length < 2) { + + require(['alert'], function (alert) { + alert({ + text: globalize.translate('sharedcomponents#PleaseSelectTwoItems'), + title: globalize.translate('sharedcomponents#Error') + }); + }); + return; + } + + var msg = globalize.translate('sharedcomponents#TheSelectedItemsWillBeGrouped'); + + require(['confirm'], function (confirm) { + + confirm(msg, globalize.translate('sharedcomponents#GroupVersions')).then(function () { + + 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 = 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 (container) { + + var self = this; + + function onTapHold(e) { + + var card = 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 initTapHold(element) { + + // mobile safari doesn't allow contextmenu override + if (browser.mobile && !browser.safari) { + container.addEventListener('contextmenu', onTapHold); + } else { + require(['hammer'], function (Hammer) { + + var manager = new Hammer.Manager(element); + + var press = new Hammer.Press({ + time: 500 + }); + + manager.add(press); + + //var hammertime = new Hammer(element); + + manager.on('press', onTapHold); + self.manager = manager; + }); + } + + showTapHoldHelp(element); + } + + initTapHold(container); + + container.addEventListener('click', onContainerClick); + + self.destroy = function () { + + container.removeEventListener('click', onContainerClick); + container.removeEventListener('contextmenu', onTapHold); + + var manager = self.manager; + if (manager) { + manager.destroy(); + self.manager = null; + } + }; + }; +}); \ No newline at end of file diff --git a/dashboard-ui/bower_components/emby-webcomponents/strings/da.json b/dashboard-ui/bower_components/emby-webcomponents/strings/da.json index 95e8f67e58..5f16d65cec 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/strings/da.json +++ b/dashboard-ui/bower_components/emby-webcomponents/strings/da.json @@ -1,8 +1,10 @@ { - "EditInfo": "Edit Info", - "RemoveFromPlaylist": "Remove from Playlist", - "PlayFromBeginning": "Play from beginning", - "ResumeAt": "Resume from {0}", + "MarkPlayed": "Mark Played", + "MarkUnplayed": "Mark Unplayed", + "GroupVersions": "Group Versions", + "PleaseSelectTwoItems": "Please select at least two items.", + "TheSelectedItemsWillBeGrouped": "The selected videos will be grouped into one virtual item. Emby apps will automatically choose which version to play based on device and network performance. Are you sure you wish to continue?", + "Error": "Error", "ValueSpecialEpisodeName": "Special - {0}", "Share": "Del", "ServerUpdateNeeded": "Denne Emby server b\u00f8r opdateres. For at downloade den nyeste version bes\u00f8g venligst {0}", @@ -109,10 +111,15 @@ "Shuffle": "Shuffle", "Identify": "Identify", "EditImages": "Edit Images", + "EditInfo": "Edit Info", "Sync": "Sync", "InstantMix": "Instant Mix", "ViewAlbum": "View Album", "ViewArtist": "View Artist", "QueueAllFromHere": "Queue All from Here", - "PlayAllFromHere": "Play All from Here" + "PlayAllFromHere": "Play All from Here", + "PlayFromBeginning": "Play from beginning", + "ResumeAt": "Resume from {0}", + "RemoveFromPlaylist": "Remove from Playlist", + "Trailer": "Trailer" } \ No newline at end of file diff --git a/dashboard-ui/bower_components/emby-webcomponents/strings/de.json b/dashboard-ui/bower_components/emby-webcomponents/strings/de.json index a80c1856dd..eb1a8f47c8 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/strings/de.json +++ b/dashboard-ui/bower_components/emby-webcomponents/strings/de.json @@ -1,8 +1,10 @@ { - "EditInfo": "Edit Info", - "RemoveFromPlaylist": "Remove from Playlist", - "PlayFromBeginning": "Play from beginning", - "ResumeAt": "Resume from {0}", + "MarkPlayed": "Mark Played", + "MarkUnplayed": "Mark Unplayed", + "GroupVersions": "Group Versions", + "PleaseSelectTwoItems": "Please select at least two items.", + "TheSelectedItemsWillBeGrouped": "The selected videos will be grouped into one virtual item. Emby apps will automatically choose which version to play based on device and network performance. Are you sure you wish to continue?", + "Error": "Error", "ValueSpecialEpisodeName": "Special - {0}", "Share": "Teilen", "ServerUpdateNeeded": "Dieser Emby Server sollte aktualisiert werden. Um die neueste Version zu laden, besuche bitte {0}", @@ -109,10 +111,15 @@ "Shuffle": "Shuffle", "Identify": "Identify", "EditImages": "Edit Images", + "EditInfo": "Edit Info", "Sync": "Sync", "InstantMix": "Instant Mix", "ViewAlbum": "View Album", "ViewArtist": "View Artist", "QueueAllFromHere": "Queue All from Here", - "PlayAllFromHere": "Play All from Here" + "PlayAllFromHere": "Play All from Here", + "PlayFromBeginning": "Play from beginning", + "ResumeAt": "Resume from {0}", + "RemoveFromPlaylist": "Remove from Playlist", + "Trailer": "Trailer" } \ No newline at end of file diff --git a/dashboard-ui/bower_components/emby-webcomponents/strings/en-US.json b/dashboard-ui/bower_components/emby-webcomponents/strings/en-US.json index 188d69b6ed..6be1d3c585 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/strings/en-US.json +++ b/dashboard-ui/bower_components/emby-webcomponents/strings/en-US.json @@ -97,7 +97,6 @@ "Favorite": "Favorite", "Like": "Like", "Dislike": "Dislike", - "Played": "Played", "RefreshDialogHelp": "Metadata is refreshed based on settings and internet services that are enabled in the Emby Server dashboard.", "Open": "Open", "Play": "Play", @@ -115,5 +114,11 @@ "PlayFromBeginning": "Play from beginning", "ResumeAt": "Resume from {0}", "RemoveFromPlaylist": "Remove from Playlist", - "Trailer": "Trailer" + "Trailer": "Trailer", + "MarkPlayed": "Mark Played", + "MarkUnplayed": "Mark Unplayed", + "GroupVersions": "Group Versions", + "PleaseSelectTwoItems": "Please select at least two items.", + "TheSelectedItemsWillBeGrouped": "The selected videos will be grouped into one virtual item. Emby apps will automatically choose which version to play based on device and network performance. Are you sure you wish to continue?", + "Error": "Error" } \ No newline at end of file diff --git a/dashboard-ui/bower_components/emby-webcomponents/strings/es-MX.json b/dashboard-ui/bower_components/emby-webcomponents/strings/es-MX.json index 0ee9d004a1..7e9f0dade7 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/strings/es-MX.json +++ b/dashboard-ui/bower_components/emby-webcomponents/strings/es-MX.json @@ -1,8 +1,10 @@ { - "EditInfo": "Edit Info", - "RemoveFromPlaylist": "Remove from Playlist", - "PlayFromBeginning": "Play from beginning", - "ResumeAt": "Resume from {0}", + "MarkPlayed": "Mark Played", + "MarkUnplayed": "Mark Unplayed", + "GroupVersions": "Group Versions", + "PleaseSelectTwoItems": "Please select at least two items.", + "TheSelectedItemsWillBeGrouped": "The selected videos will be grouped into one virtual item. Emby apps will automatically choose which version to play based on device and network performance. Are you sure you wish to continue?", + "Error": "Error", "ValueSpecialEpisodeName": "Especial - {0}", "Share": "Compartir", "ServerUpdateNeeded": "Este Servidor Emby necesita ser actualizado. Para descargar la ultima versi\u00f3n, por favor visite {0}", @@ -109,10 +111,15 @@ "Shuffle": "Shuffle", "Identify": "Identify", "EditImages": "Edit Images", + "EditInfo": "Edit Info", "Sync": "Sync", "InstantMix": "Instant Mix", "ViewAlbum": "View Album", "ViewArtist": "View Artist", "QueueAllFromHere": "Queue All from Here", - "PlayAllFromHere": "Play All from Here" + "PlayAllFromHere": "Play All from Here", + "PlayFromBeginning": "Play from beginning", + "ResumeAt": "Resume from {0}", + "RemoveFromPlaylist": "Remove from Playlist", + "Trailer": "Trailer" } \ No newline at end of file diff --git a/dashboard-ui/bower_components/emby-webcomponents/strings/fr.json b/dashboard-ui/bower_components/emby-webcomponents/strings/fr.json index 6c1a881fb8..6a0707643a 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/strings/fr.json +++ b/dashboard-ui/bower_components/emby-webcomponents/strings/fr.json @@ -1,8 +1,10 @@ { - "EditInfo": "Modifier les informations", - "RemoveFromPlaylist": "Supprimer de la liste de lecture", - "PlayFromBeginning": "Play from beginning", - "ResumeAt": "Resume from {0}", + "MarkPlayed": "Mark Played", + "MarkUnplayed": "Mark Unplayed", + "GroupVersions": "Group Versions", + "PleaseSelectTwoItems": "Please select at least two items.", + "TheSelectedItemsWillBeGrouped": "The selected videos will be grouped into one virtual item. Emby apps will automatically choose which version to play based on device and network performance. Are you sure you wish to continue?", + "Error": "Error", "ValueSpecialEpisodeName": "Sp\u00e9cial - {0}", "Share": "Partager", "ServerUpdateNeeded": "Le serveur Emby doit \u00eatre mis \u00e0 jour. Pour t\u00e9l\u00e9charger la derni\u00e8re version, veuillez visiter {0}", @@ -109,10 +111,15 @@ "Shuffle": "Lecture al\u00e9atoire", "Identify": "Identifier", "EditImages": "Modifier les images", + "EditInfo": "Modifier les informations", "Sync": "Sync", "InstantMix": "Mix instantan\u00e9", "ViewAlbum": "Voir l'album", "ViewArtist": "Voir l'artiste", "QueueAllFromHere": "Tout mette en file d'attente \u00e0 partir d'ici", - "PlayAllFromHere": "Tout lire \u00e0 partir d'ici" + "PlayAllFromHere": "Tout lire \u00e0 partir d'ici", + "PlayFromBeginning": "Play from beginning", + "ResumeAt": "Resume from {0}", + "RemoveFromPlaylist": "Supprimer de la liste de lecture", + "Trailer": "Trailer" } \ No newline at end of file diff --git a/dashboard-ui/bower_components/emby-webcomponents/strings/kk.json b/dashboard-ui/bower_components/emby-webcomponents/strings/kk.json index ce865a8b65..1643667e5b 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/strings/kk.json +++ b/dashboard-ui/bower_components/emby-webcomponents/strings/kk.json @@ -1,8 +1,10 @@ { - "EditInfo": "Edit Info", - "RemoveFromPlaylist": "Remove from Playlist", - "PlayFromBeginning": "Play from beginning", - "ResumeAt": "Resume from {0}", + "MarkPlayed": "Mark Played", + "MarkUnplayed": "Mark Unplayed", + "GroupVersions": "Group Versions", + "PleaseSelectTwoItems": "Please select at least two items.", + "TheSelectedItemsWillBeGrouped": "The selected videos will be grouped into one virtual item. Emby apps will automatically choose which version to play based on device and network performance. Are you sure you wish to continue?", + "Error": "Error", "ValueSpecialEpisodeName": "\u0410\u0440\u043d\u0430\u0439\u044b - {0}", "Share": "\u041e\u0440\u0442\u0430\u049b\u0442\u0430\u0441\u0443", "ServerUpdateNeeded": "\u041e\u0441\u044b Emby Server \u0436\u0430\u04a3\u0430\u0440\u0442\u044b\u043b\u0443\u044b \u049b\u0430\u0436\u0435\u0442. \u0421\u043e\u04a3\u0493\u044b \u043d\u04b1\u0441\u049b\u0430\u0441\u044b\u043d \u0436\u04af\u043a\u0442\u0435\u043f \u0430\u043b\u0443 \u04af\u0448\u0456\u043d, {0} \u043a\u0456\u0440\u0456\u04a3\u0456\u0437", @@ -109,10 +111,15 @@ "Shuffle": "\u0410\u0440\u0430\u043b\u0430\u0441\u0442\u044b\u0440\u0443", "Identify": "\u0410\u043d\u044b\u049b\u0442\u0430\u0443", "EditImages": "\u0421\u0443\u0440\u0435\u0442\u0442\u0435\u0440\u0434\u0456 \u04e9\u04a3\u0434\u0435\u0443", + "EditInfo": "Edit Info", "Sync": "\u04ae\u043d\u0434\u0435\u0441\u0442\u0456\u0440\u0443", "InstantMix": "\u041b\u0435\u0437\u0434\u0456\u043a \u049b\u043e\u0441\u043f\u0430", "ViewAlbum": "\u0410\u043b\u044c\u0431\u043e\u043c\u0434\u044b \u049b\u0430\u0440\u0430\u0443", "ViewArtist": "\u041e\u0440\u044b\u043d\u0434\u0430\u0443\u0448\u044b\u043d\u044b \u049b\u0430\u0440\u0430\u0443", "QueueAllFromHere": "\u0411\u04b1\u043b \u0430\u0440\u0430\u0434\u0430\u043d \u0431\u04d9\u0440\u0456\u043d \u043a\u0435\u0437\u0435\u043a\u043a\u0435", - "PlayAllFromHere": "\u0411\u04b1\u043b \u0430\u0440\u0430\u0434\u0430\u043d \u0431\u04d9\u0440\u0456\u043d \u043e\u0439\u043d\u0430\u0442\u0443" + "PlayAllFromHere": "\u0411\u04b1\u043b \u0430\u0440\u0430\u0434\u0430\u043d \u0431\u04d9\u0440\u0456\u043d \u043e\u0439\u043d\u0430\u0442\u0443", + "PlayFromBeginning": "Play from beginning", + "ResumeAt": "Resume from {0}", + "RemoveFromPlaylist": "Remove from Playlist", + "Trailer": "Trailer" } \ No newline at end of file diff --git a/dashboard-ui/bower_components/emby-webcomponents/strings/nb.json b/dashboard-ui/bower_components/emby-webcomponents/strings/nb.json index ab43e5b393..05c3dccd64 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/strings/nb.json +++ b/dashboard-ui/bower_components/emby-webcomponents/strings/nb.json @@ -1,8 +1,10 @@ { - "EditInfo": "Edit Info", - "RemoveFromPlaylist": "Remove from Playlist", - "PlayFromBeginning": "Play from beginning", - "ResumeAt": "Resume from {0}", + "MarkPlayed": "Mark Played", + "MarkUnplayed": "Mark Unplayed", + "GroupVersions": "Group Versions", + "PleaseSelectTwoItems": "Please select at least two items.", + "TheSelectedItemsWillBeGrouped": "The selected videos will be grouped into one virtual item. Emby apps will automatically choose which version to play based on device and network performance. Are you sure you wish to continue?", + "Error": "Error", "ValueSpecialEpisodeName": "Spesial - {0}", "Share": "Del", "ServerUpdateNeeded": "Denne Emby serveren trenger en oppdatering. For \u00e5 laste ned nyeste versjon, vennligst bes\u00f8k: {0}", @@ -109,10 +111,15 @@ "Shuffle": "Shuffle", "Identify": "Identify", "EditImages": "Edit Images", + "EditInfo": "Edit Info", "Sync": "Sync", "InstantMix": "Instant Mix", "ViewAlbum": "View Album", "ViewArtist": "View Artist", "QueueAllFromHere": "Queue All from Here", - "PlayAllFromHere": "Play All from Here" + "PlayAllFromHere": "Play All from Here", + "PlayFromBeginning": "Play from beginning", + "ResumeAt": "Resume from {0}", + "RemoveFromPlaylist": "Remove from Playlist", + "Trailer": "Trailer" } \ No newline at end of file diff --git a/dashboard-ui/bower_components/emby-webcomponents/strings/nl.json b/dashboard-ui/bower_components/emby-webcomponents/strings/nl.json index 9a34b2c12d..95d4d2c684 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/strings/nl.json +++ b/dashboard-ui/bower_components/emby-webcomponents/strings/nl.json @@ -1,8 +1,10 @@ { - "EditInfo": "Info Bewerken", - "RemoveFromPlaylist": "Verwijderen uit afspeellijst", - "PlayFromBeginning": "Play from beginning", - "ResumeAt": "Resume from {0}", + "MarkPlayed": "Mark Played", + "MarkUnplayed": "Mark Unplayed", + "GroupVersions": "Group Versions", + "PleaseSelectTwoItems": "Please select at least two items.", + "TheSelectedItemsWillBeGrouped": "The selected videos will be grouped into one virtual item. Emby apps will automatically choose which version to play based on device and network performance. Are you sure you wish to continue?", + "Error": "Error", "ValueSpecialEpisodeName": "Speciaal - {0}", "Share": "Delen", "ServerUpdateNeeded": "Deze Emby Server moet worden bijgewerkt. Om de laatste versie te downloaden, gaat u naar {0}", @@ -109,10 +111,15 @@ "Shuffle": "Willekeurig", "Identify": "Identificeer", "EditImages": "Bewerk afbeeldingen", + "EditInfo": "Info Bewerken", "Sync": "Synchronisatie", "InstantMix": "Instant Mix", "ViewAlbum": "Bekijk album", "ViewArtist": "Bekijk artiest", "QueueAllFromHere": "Plaats alles in de wachtrij vanaf hier", - "PlayAllFromHere": "Speel alles vanaf hier" + "PlayAllFromHere": "Speel alles vanaf hier", + "PlayFromBeginning": "Play from beginning", + "ResumeAt": "Resume from {0}", + "RemoveFromPlaylist": "Verwijderen uit afspeellijst", + "Trailer": "Trailer" } \ No newline at end of file diff --git a/dashboard-ui/bower_components/emby-webcomponents/strings/pt-BR.json b/dashboard-ui/bower_components/emby-webcomponents/strings/pt-BR.json index 1724fccd3b..1b9c85b632 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/strings/pt-BR.json +++ b/dashboard-ui/bower_components/emby-webcomponents/strings/pt-BR.json @@ -1,8 +1,10 @@ { - "EditInfo": "Editar Informa\u00e7\u00f5es", - "RemoveFromPlaylist": "Remover da Lista de Reprodu\u00e7\u00e3o", - "PlayFromBeginning": "Reproduzir do in\u00edcio", - "ResumeAt": "Retomar de {0}", + "MarkPlayed": "Mark Played", + "MarkUnplayed": "Mark Unplayed", + "GroupVersions": "Group Versions", + "PleaseSelectTwoItems": "Please select at least two items.", + "TheSelectedItemsWillBeGrouped": "The selected videos will be grouped into one virtual item. Emby apps will automatically choose which version to play based on device and network performance. Are you sure you wish to continue?", + "Error": "Error", "ValueSpecialEpisodeName": "Especial - {0}", "Share": "Compartilhar", "ServerUpdateNeeded": "Este servidor Emby precisa ser atualizado. Para baixar a \u00faltima vers\u00e3o, por favor visite {0}", @@ -109,10 +111,15 @@ "Shuffle": "Embaralhar", "Identify": "Identificar", "EditImages": "Editar Imagens", + "EditInfo": "Editar Informa\u00e7\u00f5es", "Sync": "Sincronizar", "InstantMix": "Mix Instant\u00e2neo", "ViewAlbum": "Visualizar \u00c1lbum", "ViewArtist": "Visualizar Artista", "QueueAllFromHere": "Enfileirar todos daqui", - "PlayAllFromHere": "Reproduzir todos daqui" + "PlayAllFromHere": "Reproduzir todos daqui", + "PlayFromBeginning": "Reproduzir do in\u00edcio", + "ResumeAt": "Retomar de {0}", + "RemoveFromPlaylist": "Remover da Lista de Reprodu\u00e7\u00e3o", + "Trailer": "Trailer" } \ No newline at end of file diff --git a/dashboard-ui/bower_components/emby-webcomponents/strings/pt-PT.json b/dashboard-ui/bower_components/emby-webcomponents/strings/pt-PT.json index da25029876..87d1b54880 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/strings/pt-PT.json +++ b/dashboard-ui/bower_components/emby-webcomponents/strings/pt-PT.json @@ -1,8 +1,10 @@ { - "EditInfo": "Edit Info", - "RemoveFromPlaylist": "Remove from Playlist", - "PlayFromBeginning": "Play from beginning", - "ResumeAt": "Resume from {0}", + "MarkPlayed": "Mark Played", + "MarkUnplayed": "Mark Unplayed", + "GroupVersions": "Group Versions", + "PleaseSelectTwoItems": "Please select at least two items.", + "TheSelectedItemsWillBeGrouped": "The selected videos will be grouped into one virtual item. Emby apps will automatically choose which version to play based on device and network performance. Are you sure you wish to continue?", + "Error": "Error", "ValueSpecialEpisodeName": "Especial - {0}", "Share": "Partilhar", "ServerUpdateNeeded": "Este Servidor Emby precisa ser atualizado. Para fazer download da vers\u00e3o mais recente, por favor visite {0}", @@ -109,10 +111,15 @@ "Shuffle": "Shuffle", "Identify": "Identify", "EditImages": "Edit Images", + "EditInfo": "Edit Info", "Sync": "Sync", "InstantMix": "Instant Mix", "ViewAlbum": "View Album", "ViewArtist": "View Artist", "QueueAllFromHere": "Queue All from Here", - "PlayAllFromHere": "Play All from Here" + "PlayAllFromHere": "Play All from Here", + "PlayFromBeginning": "Play from beginning", + "ResumeAt": "Resume from {0}", + "RemoveFromPlaylist": "Remove from Playlist", + "Trailer": "Trailer" } \ No newline at end of file diff --git a/dashboard-ui/bower_components/emby-webcomponents/strings/ru.json b/dashboard-ui/bower_components/emby-webcomponents/strings/ru.json index 6028c6d6c1..dfeac81d6e 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/strings/ru.json +++ b/dashboard-ui/bower_components/emby-webcomponents/strings/ru.json @@ -1,8 +1,10 @@ { - "EditInfo": "Edit Info", - "RemoveFromPlaylist": "Remove from Playlist", - "PlayFromBeginning": "Play from beginning", - "ResumeAt": "Resume from {0}", + "MarkPlayed": "Mark Played", + "MarkUnplayed": "Mark Unplayed", + "GroupVersions": "Group Versions", + "PleaseSelectTwoItems": "Please select at least two items.", + "TheSelectedItemsWillBeGrouped": "The selected videos will be grouped into one virtual item. Emby apps will automatically choose which version to play based on device and network performance. Are you sure you wish to continue?", + "Error": "Error", "ValueSpecialEpisodeName": "\u0421\u043f\u0435\u0446\u044d\u043f\u0438\u0437\u043e\u0434 - {0}", "Share": "\u041f\u043e\u0434\u0435\u043b\u0438\u0442\u044c\u0441\u044f", "ServerUpdateNeeded": "\u0414\u0430\u043d\u043d\u044b\u0439 Emby Server \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c. \u0427\u0442\u043e\u0431\u044b \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u044e\u044e \u0432\u0435\u0440\u0441\u0438\u044e, \u043f\u043e\u0441\u0435\u0442\u0438\u0442\u0435 {0}", @@ -109,10 +111,15 @@ "Shuffle": "\u041f\u0435\u0440\u0435\u043c\u0435\u0448\u0430\u0442\u044c", "Identify": "\u0420\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u0442\u044c", "EditImages": "\u041f\u0440\u0430\u0432\u0438\u0442\u044c \u0440\u0438\u0441\u0443\u043d\u043a\u0438", + "EditInfo": "Edit Info", "Sync": "\u0421\u0438\u043d\u0445\u0440\u043e", "InstantMix": "\u0410\u0432\u0442\u043e\u043c\u0438\u043a\u0441", "ViewAlbum": "\u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0430\u043b\u044c\u0431\u043e\u043c", "ViewArtist": "\u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0438\u0441\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044f", "QueueAllFromHere": "\u0412 \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u0432\u0441\u0435 \u043e\u0442\u0441\u044e\u0434\u0430", - "PlayAllFromHere": "\u0412\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0441\u0442\u0438 \u0432\u0441\u0435 \u043e\u0442\u0441\u044e\u0434\u0430" + "PlayAllFromHere": "\u0412\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0441\u0442\u0438 \u0432\u0441\u0435 \u043e\u0442\u0441\u044e\u0434\u0430", + "PlayFromBeginning": "Play from beginning", + "ResumeAt": "Resume from {0}", + "RemoveFromPlaylist": "Remove from Playlist", + "Trailer": "Trailer" } \ No newline at end of file diff --git a/dashboard-ui/bower_components/emby-webcomponents/strings/zh-TW.json b/dashboard-ui/bower_components/emby-webcomponents/strings/zh-TW.json index 66c4792126..13a2dee9f8 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/strings/zh-TW.json +++ b/dashboard-ui/bower_components/emby-webcomponents/strings/zh-TW.json @@ -1,8 +1,10 @@ { - "EditInfo": "Edit Info", - "RemoveFromPlaylist": "Remove from Playlist", - "PlayFromBeginning": "Play from beginning", - "ResumeAt": "Resume from {0}", + "MarkPlayed": "Mark Played", + "MarkUnplayed": "Mark Unplayed", + "GroupVersions": "Group Versions", + "PleaseSelectTwoItems": "Please select at least two items.", + "TheSelectedItemsWillBeGrouped": "The selected videos will be grouped into one virtual item. Emby apps will automatically choose which version to play based on device and network performance. Are you sure you wish to continue?", + "Error": "Error", "ValueSpecialEpisodeName": "Special - {0}", "Share": "\u5206\u4eab", "ServerUpdateNeeded": "\u6b64Emby\u4f3a\u670d\u5668\u9700\u8981\u66f4\u65b0\uff0c\u8acb\u81f3{0}\u53d6\u5f97\u6700\u65b0\u7248\u672c", @@ -109,10 +111,15 @@ "Shuffle": "Shuffle", "Identify": "Identify", "EditImages": "Edit Images", + "EditInfo": "Edit Info", "Sync": "Sync", "InstantMix": "Instant Mix", "ViewAlbum": "View Album", "ViewArtist": "View Artist", "QueueAllFromHere": "Queue All from Here", - "PlayAllFromHere": "Play All from Here" + "PlayAllFromHere": "Play All from Here", + "PlayFromBeginning": "Play from beginning", + "ResumeAt": "Resume from {0}", + "RemoveFromPlaylist": "Remove from Playlist", + "Trailer": "Trailer" } \ No newline at end of file diff --git a/dashboard-ui/components/remotecontrol.js b/dashboard-ui/components/remotecontrol.js index b71c4745a8..b52461c2aa 100644 --- a/dashboard-ui/components/remotecontrol.js +++ b/dashboard-ui/components/remotecontrol.js @@ -1,4 +1,4 @@ -define(['browser', 'datetime', 'libraryBrowser', 'listView'], function (browser, datetime, libraryBrowser, listView) { +define(['browser', 'datetime', 'libraryBrowser', 'listView', 'userdataButtons'], function (browser, datetime, libraryBrowser, listView, userdataButtons) { function showSlideshowMenu(context) { require(['scripts/slideshow'], function () { @@ -202,7 +202,11 @@ } ApiClient.getItem(Dashboard.getCurrentUserId(), item.Id).then(function (fullItem) { - context.querySelector('.nowPlayingPageUserDataButtons').innerHTML = libraryBrowser.getUserDataIconsHtml(fullItem, false); + context.querySelector('.nowPlayingPageUserDataButtons').innerHTML = userdataButtons.getIconsHtml({ + item: fullItem, + includePlayed: false, + style: 'fab-mini' + }); }); } else { context.querySelector('.nowPlayingPageUserDataButtons').innerHTML = ''; diff --git a/dashboard-ui/css/librarybrowser.css b/dashboard-ui/css/librarybrowser.css index 42c17211a0..abf8f1e188 100644 --- a/dashboard-ui/css/librarybrowser.css +++ b/dashboard-ui/css/librarybrowser.css @@ -1030,54 +1030,6 @@ span.itemCommunityRating:not(:empty) + .userDataIcons { right: 20px; } -.selectedCharacter { - color: #52B54B !important; -} - -.itemOverlayHtml { - max-height: 100px; - margin-top: 1.5em; - overflow: hidden; - text-overflow: ellipsis; -} - -.itemSelectionPanel { - position: absolute; - bottom: 0; - left: 0; - right: 0; - top: 0; - background-color: rgba(0, 0, 0, .3); - z-index: 999; - border: 1px solid #43A047; -} - - .itemSelectionPanel #checkbox { - border-radius: 0 !important; - } - -.selectionCommandsPanel { - position: fixed; - top: 0; - left: 0; - right: 0; - height: 50px; - background: #43A047; - z-index: 1000; - padding: 0 .75em 0 .25em; - display: flex; - align-items: center; - color: #fff; -} - -.itemSelectionCount { - font-size: 28px; - vertical-align: middle; - color: #fff; - display: inline-block; - padding-top: 1px; -} - @media all and (max-height: 480px) { .alphabetPicker { diff --git a/dashboard-ui/scripts/itemdetailpage.js b/dashboard-ui/scripts/itemdetailpage.js index 41bcae2188..80786bb5a1 100644 --- a/dashboard-ui/scripts/itemdetailpage.js +++ b/dashboard-ui/scripts/itemdetailpage.js @@ -1,4 +1,4 @@ -define(['layoutManager', 'datetime', 'mediaInfo', 'backdrop', 'listView', 'itemContextMenu', 'itemHelper', 'scrollStyles', 'emby-itemscontainer'], function (layoutManager, datetime, mediaInfo, backdrop, listView, itemContextMenu, itemHelper) { +define(['layoutManager', 'datetime', 'mediaInfo', 'backdrop', 'listView', 'itemContextMenu', 'itemHelper', 'userdataButtons', 'scrollStyles', 'emby-itemscontainer'], function (layoutManager, datetime, mediaInfo, backdrop, listView, itemContextMenu, itemHelper, userdataButtons) { var currentItem; @@ -1161,7 +1161,10 @@ var userDataIcons = page.querySelectorAll('.userDataIcons'); - var html = LibraryBrowser.getUserDataIconsHtml(item, true, 'icon-button'); + var html = userdataButtons.getIconsHtml({ + item: item, + style: 'fab-mini' + }); for (var i = 0, length = userDataIcons.length; i < length; i++) { userDataIcons[i].innerHTML = html; @@ -1973,17 +1976,6 @@ }); } - function onItemDeleted(e, itemId) { - - if (currentItem && currentItem.Id == itemId) { - if (currentItem.Type == 'Recording') { - LibraryBrowser.showTab('livetv.html', 3); - } else { - Dashboard.navigate('home.html'); - } - } - } - function showPlayMenu(item, target) { require(['playMenu'], function (playMenu) { @@ -2218,12 +2210,9 @@ reload(page, params); Events.on(ApiClient, 'websocketmessage', onWebSocketMessage); - - Events.on(LibraryBrowser, 'itemdeleting', onItemDeleted); }); view.addEventListener('viewbeforehide', function () { - Events.off(LibraryBrowser, 'itemdeleting', onItemDeleted); currentItem = null; diff --git a/dashboard-ui/scripts/librarybrowser.js b/dashboard-ui/scripts/librarybrowser.js index cccea25270..c755c8ed1d 100644 --- a/dashboard-ui/scripts/librarybrowser.js +++ b/dashboard-ui/scripts/librarybrowser.js @@ -577,34 +577,6 @@ }); }, - deleteItems: function (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); - Events.trigger(LibraryBrowser, 'itemdeleting', [itemId]); - }); - - resolve(); - }, reject); - - }); - }); - }, - editImages: function (itemId) { return new Promise(function (resolve, reject) { @@ -2265,80 +2237,6 @@ return null; }, - getUserDataButtonHtml: function (method, itemId, btnCssClass, icon, tooltip, style) { - - if (style == 'fab') { - - var tagName = 'paper-fab'; - return '<' + tagName + ' title="' + tooltip + '" data-id="' + itemId + '" icon="' + icon + '" class="' + btnCssClass + '" onclick="LibraryBrowser.' + method + '(this);return false;">'; - } - - return ''; - }, - - getUserDataIconsHtml: function (item, includePlayed, style) { - - var html = ''; - - var userData = item.UserData || {}; - - var itemId = item.Id; - - if (includePlayed !== false) { - var tooltipPlayed = Globalize.translate('TooltipPlayed'); - - if (item.MediaType == 'Video' || item.Type == 'Series' || item.Type == 'Season' || item.Type == 'BoxSet' || item.Type == 'Playlist') { - if (item.Type != 'TvChannel') { - if (userData.Played) { - html += LibraryBrowser.getUserDataButtonHtml('markPlayed', itemId, 'btnUserItemRating btnUserItemRatingOn', 'check', tooltipPlayed, style); - } else { - html += LibraryBrowser.getUserDataButtonHtml('markPlayed', itemId, 'btnUserItemRating', 'check', tooltipPlayed, style); - } - } - } - } - - var tooltipFavorite = Globalize.translate('TooltipFavorite'); - if (userData.IsFavorite) { - - html += LibraryBrowser.getUserDataButtonHtml('markFavorite', itemId, 'btnUserItemRating btnUserItemRatingOn', 'favorite', tooltipFavorite, style); - } else { - html += LibraryBrowser.getUserDataButtonHtml('markFavorite', itemId, 'btnUserItemRating', 'favorite', tooltipFavorite, style); - } - - return html; - }, - - markPlayed: function (link) { - - var id = link.getAttribute('data-id'); - - var markAsPlayed = !link.classList.contains('btnUserItemRatingOn'); - - if (markAsPlayed) { - ApiClient.markPlayed(Dashboard.getCurrentUserId(), id); - link.classList.add('btnUserItemRatingOn'); - } else { - ApiClient.markUnplayed(Dashboard.getCurrentUserId(), id); - link.classList.remove('btnUserItemRatingOn'); - } - }, - - markFavorite: function (link) { - - var id = link.getAttribute('data-id'); - - var markAsFavorite = !link.classList.contains('btnUserItemRatingOn'); - - ApiClient.updateFavoriteStatus(Dashboard.getCurrentUserId(), id, markAsFavorite); - - if (markAsFavorite) { - link.classList.add('btnUserItemRatingOn'); - } else { - link.classList.remove('btnUserItemRatingOn'); - } - }, - renderDetailImage: function (elem, item, editable, preferThumb) { var imageTags = item.ImageTags || {}; diff --git a/dashboard-ui/scripts/librarylist.js b/dashboard-ui/scripts/librarylist.js index 5934b796b1..80acb79742 100644 --- a/dashboard-ui/scripts/librarylist.js +++ b/dashboard-ui/scripts/librarylist.js @@ -1,4 +1,4 @@ -define(['appSettings', 'appStorage', 'libraryBrowser', 'apphost', 'itemHelper'], function (appSettings, appStorage, LibraryBrowser, appHost, itemHelper) { +define(['libraryBrowser', 'itemHelper'], function (libraryBrowser, itemHelper) { function isClickable(target) { @@ -56,7 +56,7 @@ ApiClient.getJSON(ApiClient.getUrl('Users/' + userId + '/Items/Latest', options)).then(function (items) { if (items.length == 1) { - Dashboard.navigate(LibraryBrowser.getHref(items[0], context)); + Dashboard.navigate(libraryBrowser.getHref(items[0], context)); return; } @@ -86,450 +86,12 @@ return elem; } - LibraryBrowser.createCardMenus = function (curr, options) { + libraryBrowser.createCardMenus = function (curr, options) { curr.removeEventListener('click', onCardClick); curr.addEventListener('click', onCardClick); - - //initTapHoldMenus(curr); }; - function initTapHoldMenus(elem) { - - if (elem.classList.contains('itemsContainer')) { - initTapHold(elem); - return; - } - - var elems = elem.querySelectorAll('.itemsContainer'); - - for (var i = 0, length = elems.length; i < length; i++) { - initTapHold(elems[i]); - } - } - - function initTapHold(element) { - - if (!LibraryBrowser.allowSwipe(element)) { - return; - } - - if (element.classList.contains('hasTapHold')) { - return; - } - - require(['hammer'], function (Hammer) { - - var manager = new Hammer.Manager(element); - - var press = new Hammer.Press({ - time: 500 - }); - - manager.add(press); - - //var hammertime = new Hammer(element); - element.classList.add('hasTapHold'); - - manager.on('press', onTapHold); - }); - - showTapHoldHelp(element); - } - - function showTapHoldHelp(element) { - - var page = parentWithClass(element, 'page'); - - if (!page) { - return; - } - - // Don't do this on the home page - if (page.classList.contains('homePage') || page.classList.contains('itemDetailPage') || page.classList.contains('liveTvPage')) { - return; - } - - var expectedValue = "8"; - if (appStorage.getItem("tapholdhelp") == expectedValue) { - return; - } - - appStorage.setItem("tapholdhelp", expectedValue); - - Dashboard.alert({ - message: Globalize.translate('TryMultiSelectMessage'), - title: Globalize.translate('HeaderTryMultiSelect') - }); - } - - function onTapHold(e) { - - var card = parentWithClass(e.target, 'card'); - - if (card) { - - showSelections(card); - - // It won't have this if it's a hammer event - if (e.stopPropagation) { - e.stopPropagation(); - } - e.preventDefault(); - return false; - } - e.preventDefault(); - // It won't have this if it's a hammer event - if (e.stopPropagation) { - e.stopPropagation(); - } - return false; - } - - function onItemSelectionPanelClick(e, itemSelectionPanel) { - - // toggle the checkbox, if it wasn't clicked on - if (!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 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'); - - item.querySelector('.cardContent').appendChild(itemSelectionPanel); - - var cssClass = 'chkItemSelect'; - if (isChecked && !browserInfo.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 = document.querySelector('.selectionCommandsPanel'); - - if (!selectionCommandsPanel) { - - selectionCommandsPanel = document.createElement('div'); - selectionCommandsPanel.classList.add('selectionCommandsPanel'); - - document.body.appendChild(selectionCommandsPanel); - - var html = ''; - - html += '
'; - html += ''; - html += ''; - html += '
'; - - html += ''; - - selectionCommandsPanel.innerHTML = html; - - selectionCommandsPanel.querySelector('.btnCloseSelectionPanel').addEventListener('click', hideSelections); - - var btnSelectionPanelOptions = selectionCommandsPanel.querySelector('.btnSelectionPanelOptions'); - - btnSelectionPanelOptions.addEventListener('click', showMenuForSelectedItems); - - if (!browserInfo.mobile) { - shake(btnSelectionPanelOptions, 1); - } - } - } - - function shake(elem, iterations) { - var keyframes = [ - { transform: 'translate3d(0, 0, 0)', offset: 0 }, - { transform: 'translate3d(-10px, 0, 0)', offset: 0.1 }, - { transform: 'translate3d(10px, 0, 0)', offset: 0.2 }, - { transform: 'translate3d(-10px, 0, 0)', offset: 0.3 }, - { transform: 'translate3d(10px, 0, 0)', offset: 0.4 }, - { transform: 'translate3d(-10px, 0, 0)', offset: 0.5 }, - { transform: 'translate3d(10px, 0, 0)', offset: 0.6 }, - { transform: 'translate3d(-10px, 0, 0)', offset: 0.7 }, - { transform: 'translate3d(10px, 0, 0)', offset: 0.8 }, - { transform: 'translate3d(-10px, 0, 0)', offset: 0.9 }, - { transform: 'translate3d(0, 0, 0)', offset: 1 }]; - var timing = { duration: 900, iterations: iterations }; - - if (elem.animate) { - elem.animate(keyframes, timing); - } - } - - 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 hideSelections() { - - var selectionCommandsPanel = document.querySelector('.selectionCommandsPanel'); - if (selectionCommandsPanel) { - - selectionCommandsPanel.parentNode.removeChild(selectionCommandsPanel); - - selectedItems = []; - var elems = document.querySelectorAll('.itemSelectionPanel'); - for (var i = 0, length = elems.length; i < length; i++) { - elems[i].parentNode.removeChild(elems[i]); - } - } - } - - var selectedItems = []; - function updateItemSelection(chkItemSelect, selected) { - - var id = parentWithClass(chkItemSelect, 'card').getAttribute('data-id'); - - if (selected) { - - var current = selectedItems.filter(function (i) { - return i == id; - }); - - if (!current.length) { - selectedItems.push(id); - } - - } else { - selectedItems = selectedItems.filter(function (i) { - return i != id; - }); - } - - if (selectedItems.length) { - var itemSelectionCount = document.querySelector('.itemSelectionCount'); - if (itemSelectionCount) { - itemSelectionCount.innerHTML = selectedItems.length; - } - } else { - hideSelections(); - } - } - - function showMenuForSelectedItems(e) { - - Dashboard.getCurrentUser().then(function (user) { - - var items = []; - - items.push({ - name: Globalize.translate('ButtonAddToCollection'), - id: 'addtocollection', - ironIcon: 'add' - }); - - items.push({ - name: Globalize.translate('ButtonAddToPlaylist'), - id: 'playlist', - ironIcon: 'playlist-add' - }); - - if (user.Policy.EnableContentDeletion) { - items.push({ - name: Globalize.translate('ButtonDelete'), - id: 'delete', - ironIcon: 'delete' - }); - } - - if (user.Policy.EnableContentDownloading && appHost.supports('filedownload')) { - //items.push({ - // name: Globalize.translate('ButtonDownload'), - // id: 'download', - // ironIcon: 'file-download' - //}); - } - - items.push({ - name: Globalize.translate('HeaderGroupVersions'), - id: 'groupvideos', - ironIcon: 'call-merge' - }); - - items.push({ - name: Globalize.translate('MarkPlayed'), - id: 'markplayed' - }); - - items.push({ - name: Globalize.translate('MarkUnplayed'), - id: 'markunplayed' - }); - - items.push({ - name: Globalize.translate('ButtonRefresh'), - id: 'refresh', - ironIcon: 'refresh' - }); - - items.push({ - name: Globalize.translate('ButtonSync'), - id: 'sync', - ironIcon: 'sync' - }); - - require(['actionsheet'], function (actionsheet) { - - actionsheet.show({ - items: items, - 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(); - break; - case 'playlist': - require(['playlistEditor'], function (playlistEditor) { - new playlistEditor().show({ - items: items, - serverId: serverId - }); - }); - hideSelections(); - break; - case 'delete': - LibraryBrowser.deleteItems(items).then(function () { - Dashboard.navigate('home.html'); - }); - hideSelections(); - break; - case 'groupvideos': - combineVersions(parentWithClass(e.target, 'page'), items); - break; - case 'markplayed': - items.forEach(function (itemId) { - ApiClient.markPlayed(Dashboard.getCurrentUserId(), itemId); - }); - hideSelections(); - break; - case 'markunplayed': - items.forEach(function (itemId) { - ApiClient.markUnplayed(Dashboard.getCurrentUserId(), itemId); - }); - hideSelections(); - break; - case 'refresh': - require(['refreshDialog'], function (refreshDialog) { - new refreshDialog({ - itemIds: items, - serverId: serverId - }).show(); - }); - hideSelections(); - break; - case 'sync': - require(['syncDialog'], function (syncDialog) { - syncDialog.showMenu({ - items: items.map(function (i) { - return { - Id: i - }; - }) - }); - }); - hideSelections(); - break; - default: - break; - } - } - }); - - }); - }); - } - - function combineVersions(page, selection) { - - if (selection.length < 2) { - - Dashboard.alert({ - message: Globalize.translate('MessagePleaseSelectTwoItems'), - title: Globalize.translate('HeaderError') - }); - - return; - } - - var msg = Globalize.translate('MessageTheSelectedItemsWillBeGrouped'); - - require(['confirm'], function (confirm) { - - confirm(msg, Globalize.translate('HeaderGroupVersions')).then(function () { - - Dashboard.showLoadingMsg(); - - ApiClient.ajax({ - - type: "POST", - url: ApiClient.getUrl("Videos/MergeVersions", { Ids: selection.join(',') }) - - }).then(function () { - - Dashboard.hideLoadingMsg(); - hideSelections(); - page.querySelector('.itemsContainer').dispatchEvent(new CustomEvent('needsrefresh', {})); - }); - }); - }); - } - function showSyncButtonsPerUser(page) { var apiClient = window.ApiClient; @@ -587,12 +149,4 @@ showSyncButtonsPerUser(page); } }); - - pageClassOn('pagebeforehide', "libraryPage", function () { - - var page = this; - - hideSelections(); - }); - }); \ No newline at end of file diff --git a/dashboard-ui/scripts/nowplayingbar.js b/dashboard-ui/scripts/nowplayingbar.js index 414677a2f7..5fc788eac7 100644 --- a/dashboard-ui/scripts/nowplayingbar.js +++ b/dashboard-ui/scripts/nowplayingbar.js @@ -1,4 +1,4 @@ -define(['datetime', 'paper-icon-button-light'], function (datetime) { +define(['datetime', 'userdataButtons', 'paper-icon-button-light'], function (datetime, userdataButtons) { var currentPlayer; @@ -572,7 +572,10 @@ if (nowPlayingItem.Id) { ApiClient.getItem(Dashboard.getCurrentUserId(), nowPlayingItem.Id).then(function (item) { - nowPlayingUserData.innerHTML = LibraryBrowser.getUserDataIconsHtml(item, false); + nowPlayingUserData.innerHTML = userdataButtons.getIconsHtml({ + item: item, + includePlayed: false + }); }); } else { nowPlayingUserData.innerHTML = ''; diff --git a/dashboard-ui/scripts/playlistedit.js b/dashboard-ui/scripts/playlistedit.js index 9ed5d0d858..4cb057da67 100644 --- a/dashboard-ui/scripts/playlistedit.js +++ b/dashboard-ui/scripts/playlistedit.js @@ -135,26 +135,6 @@ }); } - function showDragAndDropHelp() { - - if (AppInfo.isTouchPreferred) { - // Not implemented for mobile yet - return; - } - - var expectedValue = "7"; - if (appStorage.getItem("playlistitemdragdrophelp") == expectedValue) { - return; - } - - appStorage.setItem("playlistitemdragdrophelp", expectedValue); - - Dashboard.alert({ - message: Globalize.translate('TryDragAndDropMessage'), - title: Globalize.translate('HeaderTryDragAndDrop') - }); - } - function init(page, item) { var elem = page.querySelector('#childrenContent .itemsContainer'); @@ -174,7 +154,6 @@ } reloadItems(page, item); - showDragAndDropHelp(); } }; diff --git a/dashboard-ui/scripts/site.js b/dashboard-ui/scripts/site.js index 14b1ff7148..b439ca4613 100644 --- a/dashboard-ui/scripts/site.js +++ b/dashboard-ui/scripts/site.js @@ -1805,6 +1805,7 @@ var AppInfo = {}; define("emby-button", [embyWebComponentsBowerPath + "/emby-button/emby-button"], returnFirstDependency); define("emby-itemscontainer", [embyWebComponentsBowerPath + "/emby-itemscontainer/emby-itemscontainer"], returnFirstDependency); define("itemHoverMenu", [embyWebComponentsBowerPath + "/itemhovermenu/itemhovermenu"], returnFirstDependency); + define("multiSelect", [embyWebComponentsBowerPath + "/multiselect/multiselect"], returnFirstDependency); define("alphaPicker", [embyWebComponentsBowerPath + "/alphapicker/alphapicker"], returnFirstDependency); define("paper-icon-button-light", [embyWebComponentsBowerPath + "/emby-button/paper-icon-button-light"]); diff --git a/dashboard-ui/strings/en-US.json b/dashboard-ui/strings/en-US.json index eb61b12db7..ac576a7ed3 100644 --- a/dashboard-ui/strings/en-US.json +++ b/dashboard-ui/strings/en-US.json @@ -1699,7 +1699,6 @@ "MessageChromecastConnectionError": "Your Chromecast receiver is unable to connect to your Emby Server. Please check their connections and try again.", "MessagePleaseSelectOneItem": "Please select at least one item.", "MessagePleaseSelectTwoItems": "Please select at least two items.", - "MessageTheSelectedItemsWillBeGrouped": "The selected videos will be grouped into one virtual item. Emby apps will automatically choose which version to play based on device and network performance. Are you sure you wish to continue?", "HeaderLibraryFolders": "Media Folders", "HeaderFavoriteMovies": "Favorite Movies", "HeaderFavoriteShows": "Favorite Shows", @@ -2285,8 +2284,6 @@ "NumLocationsValue": "{0} folders", "ButtonAddMediaLibrary": "Add Media Library", "ButtonManageFolders": "Manage folders", - "HeaderTryDragAndDrop": "Try Drag and Drop", - "TryDragAndDropMessage": "To re-arrange playlist items, just drag and drop. Try it!", "HeaderTryMicrosoftEdge": "Try Microsoft Edge", "MessageTryMicrosoftEdge": "For a better experience on Windows 10, try the new Microsoft Edge Browser.", "HeaderTryModernBrowser": "Try a Modern Web Browser",