define(['itemShortcuts', 'inputManager', 'connectionManager', 'playbackManager', 'imageLoader', 'layoutManager', 'browser', 'dom', 'loading', 'focusManager', 'serverNotifications', 'events', 'registerElement'], function (itemShortcuts, inputManager, connectionManager, playbackManager, imageLoader, layoutManager, browser, dom, loading, focusManager, serverNotifications, events) { 'use strict'; var ItemsContainerPrototype = Object.create(HTMLDivElement.prototype); function onClick(e) { var itemsContainer = this; var target = e.target; var multiSelect = itemsContainer.multiSelect; if (multiSelect) { if (multiSelect.onContainerClick.call(itemsContainer, e) === false) { return; } } itemShortcuts.onClick.call(itemsContainer, e); } function disableEvent(e) { e.preventDefault(); e.stopPropagation(); return false; } function onContextMenu(e) { var itemsContainer = this; var target = e.target; var card = dom.parentWithAttribute(target, 'data-id'); // check for serverId, it won't be present on selectserver if (card && card.getAttribute('data-serverid')) { inputManager.trigger('menu', { sourceElement: card }); e.preventDefault(); e.stopPropagation(); return false; } } function getShortcutOptions() { return { click: false }; } ItemsContainerPrototype.enableMultiSelect = function (enabled) { var current = this.multiSelect; if (!enabled) { if (current) { current.destroy(); this.multiSelect = null; } return; } if (current) { return; } var self = this; require(['multiSelect'], function (MultiSelect) { self.multiSelect = new MultiSelect({ container: self, bindOnClick: false }); }); }; function onDrop(evt, itemsContainer) { var el = evt.item; var newIndex = evt.newIndex; var itemId = el.getAttribute('data-playlistitemid'); var playlistId = el.getAttribute('data-playlistid'); if (!playlistId) { var oldIndex = evt.oldIndex; el.dispatchEvent(new CustomEvent('itemdrop', { detail: { oldIndex: oldIndex, newIndex: newIndex, playlistItemId: itemId }, bubbles: true, cancelable: false })); return; } var serverId = el.getAttribute('data-serverid'); var apiClient = connectionManager.getApiClient(serverId); loading.show(); apiClient.ajax({ url: apiClient.getUrl('Playlists/' + playlistId + '/Items/' + itemId + '/Move/' + newIndex), type: 'POST' }).then(function () { loading.hide(); }, function () { loading.hide(); itemsContainer.refreshItems(); }); } ItemsContainerPrototype.enableDragReordering = function (enabled) { var current = this.sortable; if (!enabled) { if (current) { current.destroy(); this.sortable = null; } return; } if (current) { return; } var self = this; require(['sortable'], function (Sortable) { self.sortable = new Sortable(self, { draggable: ".listItem", handle: '.listViewDragHandle', // dragging ended onEnd: function (evt) { return onDrop(evt, self); } }); }); }; function onUserDataChanged(e, apiClient, userData) { var itemsContainer = this; require(['cardBuilder'], function (cardBuilder) { cardBuilder.onUserDataChanged(userData, itemsContainer); }); var eventsToMonitor = getEventsToMonitor(itemsContainer); // TODO: Check user data change reason? if (eventsToMonitor.indexOf('markfavorite') !== -1) { itemsContainer.notifyRefreshNeeded(); } else if (eventsToMonitor.indexOf('markplayed') !== -1) { itemsContainer.notifyRefreshNeeded(); } } function getEventsToMonitor(itemsContainer) { var monitor = itemsContainer.getAttribute('data-monitor'); if (monitor) { return monitor.split(','); } return []; } function onTimerCreated(e, apiClient, data) { var itemsContainer = this; if (getEventsToMonitor(itemsContainer).indexOf('timers') !== -1) { itemsContainer.notifyRefreshNeeded(); return; } var programId = data.ProgramId; // This could be null, not supported by all tv providers var newTimerId = data.Id; require(['cardBuilder'], function (cardBuilder) { cardBuilder.onTimerCreated(programId, newTimerId, itemsContainer); }); } function onSeriesTimerCreated(e, apiClient, data) { var itemsContainer = this; if (getEventsToMonitor(itemsContainer).indexOf('seriestimers') !== -1) { itemsContainer.notifyRefreshNeeded(); return; } } function onTimerCancelled(e, apiClient, data) { var itemsContainer = this; if (getEventsToMonitor(itemsContainer).indexOf('timers') !== -1) { itemsContainer.notifyRefreshNeeded(); return; } require(['cardBuilder'], function (cardBuilder) { cardBuilder.onTimerCancelled(data.Id, itemsContainer); }); } function onSeriesTimerCancelled(e, apiClient, data) { var itemsContainer = this; if (getEventsToMonitor(itemsContainer).indexOf('seriestimers') !== -1) { itemsContainer.notifyRefreshNeeded(); return; } require(['cardBuilder'], function (cardBuilder) { cardBuilder.onSeriesTimerCancelled(data.Id, itemsContainer); }); } function onLibraryChanged(e, apiClient, data) { var itemsContainer = this; var eventsToMonitor = getEventsToMonitor(itemsContainer); if (eventsToMonitor.indexOf('seriestimers') !== -1 || eventsToMonitor.indexOf('timers') !== -1) { // yes this is an assumption return; } var itemsAdded = data.ItemsAdded || []; var itemsRemoved = data.ItemsRemoved || []; if (!itemsAdded.length && !itemsRemoved.length) { return; } var parentId = itemsContainer.getAttribute('data-parentid'); if (parentId) { var foldersAddedTo = data.FoldersAddedTo || []; var foldersRemovedFrom = data.FoldersRemovedFrom || []; var collectionFolders = data.CollectionFolders || []; if (foldersAddedTo.indexOf(parentId) === -1 && foldersRemovedFrom.indexOf(parentId) === -1 && collectionFolders.indexOf(parentId) === -1) { return; } } itemsContainer.notifyRefreshNeeded(); } function onPlaybackStopped(e, stopInfo) { var itemsContainer = this; var state = stopInfo.state; var eventsToMonitor = getEventsToMonitor(itemsContainer); if (state.NowPlayingItem && state.NowPlayingItem.MediaType === 'Video') { if (eventsToMonitor.indexOf('videoplayback') !== -1) { itemsContainer.notifyRefreshNeeded(true); return; } } else if (state.NowPlayingItem && state.NowPlayingItem.MediaType === 'Audio') { if (eventsToMonitor.indexOf('audioplayback') !== -1) { itemsContainer.notifyRefreshNeeded(true); return; } } } function addNotificationEvent(instance, name, handler, owner) { var localHandler = handler.bind(instance); owner = owner || serverNotifications; events.on(owner, name, localHandler); instance['event_' + name] = localHandler; } function removeNotificationEvent(instance, name, owner) { var handler = instance['event_' + name]; if (handler) { owner = owner || serverNotifications; events.off(owner, name, handler); instance['event_' + name] = null; } } ItemsContainerPrototype.createdCallback = function () { this.classList.add('itemsContainer'); }; ItemsContainerPrototype.attachedCallback = function () { this.addEventListener('click', onClick); if (browser.touch) { this.addEventListener('contextmenu', disableEvent); } else { if (this.getAttribute('data-contextmenu') !== 'false') { this.addEventListener('contextmenu', onContextMenu); } } if (layoutManager.desktop || layoutManager.mobile) { if (this.getAttribute('data-multiselect') !== 'false') { this.enableMultiSelect(true); } } if (layoutManager.tv) { this.classList.add('itemsContainer-tv'); } itemShortcuts.on(this, getShortcutOptions()); addNotificationEvent(this, 'UserDataChanged', onUserDataChanged); addNotificationEvent(this, 'TimerCreated', onTimerCreated); addNotificationEvent(this, 'SeriesTimerCreated', onSeriesTimerCreated); addNotificationEvent(this, 'TimerCancelled', onTimerCancelled); addNotificationEvent(this, 'SeriesTimerCancelled', onSeriesTimerCancelled); addNotificationEvent(this, 'LibraryChanged', onLibraryChanged); addNotificationEvent(this, 'playbackstop', onPlaybackStopped, playbackManager); if (this.getAttribute('data-dragreorder') === 'true') { this.enableDragReordering(true); } }; ItemsContainerPrototype.detachedCallback = function () { clearRefreshInterval(this); this.enableMultiSelect(false); this.enableDragReordering(false); this.removeEventListener('click', onClick); this.removeEventListener('contextmenu', onContextMenu); this.removeEventListener('contextmenu', disableEvent); itemShortcuts.off(this, getShortcutOptions()); removeNotificationEvent(this, 'UserDataChanged'); removeNotificationEvent(this, 'TimerCreated'); removeNotificationEvent(this, 'SeriesTimerCreated'); removeNotificationEvent(this, 'TimerCancelled'); removeNotificationEvent(this, 'SeriesTimerCancelled'); removeNotificationEvent(this, 'LibraryChanged'); removeNotificationEvent(this, 'playbackstop', playbackManager); this.fetchData = null; this.getItemsHtml = null; this.parentContainer = null; }; ItemsContainerPrototype.pause = function () { clearRefreshInterval(this, true); this.paused = true; }; ItemsContainerPrototype.resume = function (options) { this.paused = false; var refreshIntervalEndTime = this.refreshIntervalEndTime; if (refreshIntervalEndTime) { var remainingMs = refreshIntervalEndTime - new Date().getTime(); if (remainingMs > 0 && !this.needsRefresh) { resetRefreshInterval(this, remainingMs); } else { this.needsRefresh = true; this.refreshIntervalEndTime = null; } } if (this.needsRefresh || (options && options.refresh)) { return this.refreshItems(); } return Promise.resolve(); }; ItemsContainerPrototype.refreshItems = function () { if (!this.fetchData) { return Promise.resolve(); } if (this.paused) { this.needsRefresh = true; return Promise.resolve(); } this.needsRefresh = false; return this.fetchData().then(onDataFetched.bind(this)); }; ItemsContainerPrototype.notifyRefreshNeeded = function (isInForeground) { if (this.paused) { this.needsRefresh = true; return; } var timeout = this.refreshTimeout; if (timeout) { clearTimeout(timeout); } if (isInForeground === true) { this.refreshItems(); } else { this.refreshTimeout = setTimeout(this.refreshItems.bind(this), 10000); } }; function clearRefreshInterval(itemsContainer, isPausing) { if (itemsContainer.refreshInterval) { clearInterval(itemsContainer.refreshInterval); itemsContainer.refreshInterval = null; if (!isPausing) { itemsContainer.refreshIntervalEndTime = null; } } } function resetRefreshInterval(itemsContainer, intervalMs) { clearRefreshInterval(itemsContainer); if (!intervalMs) { intervalMs = parseInt(itemsContainer.getAttribute('data-refreshinterval') || '0'); } if (intervalMs) { itemsContainer.refreshInterval = setInterval(itemsContainer.notifyRefreshNeeded.bind(itemsContainer), intervalMs); itemsContainer.refreshIntervalEndTime = new Date().getTime() + intervalMs; } } function onDataFetched(result) { var items = result.Items || result; var parentContainer = this.parentContainer; if (parentContainer) { if (items.length) { parentContainer.classList.remove('hide'); } else { parentContainer.classList.add('hide'); } } var activeElement = document.activeElement; var focusId; var hasActiveElement; if (this.contains(activeElement)) { hasActiveElement = true; focusId = activeElement.getAttribute('data-id'); } this.innerHTML = this.getItemsHtml(items); imageLoader.lazyChildren(this); if (hasActiveElement) { setFocus(this, focusId); } resetRefreshInterval(this); if (this.afterRefresh) { this.afterRefresh(result); } } function setFocus(itemsContainer, focusId) { if (focusId) { var newElement = itemsContainer.querySelector('[data-id="' + focusId + '"]'); if (newElement) { try { focusManager.focus(newElement); return; } catch (err) { console.error(err); } } } focusManager.autoFocus(itemsContainer); } document.registerElement('emby-itemscontainer', { prototype: ItemsContainerPrototype, extends: 'div' }); });