From 0dacc82ab71b21f9c8ac39fe6cb5679914a2ad0e Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 15 Nov 2016 14:42:43 -0500 Subject: [PATCH] rework genre views --- .../emby-webcomponents/.bower.json | 8 +- .../emby-webcomponents/images/imagehelper.js | 167 +--------------- .../emby-webcomponents/images/lazyloader.js | 182 ++++++++++++++++++ .../emby-webcomponents/strings/nl.json | 64 +++--- .../emby-webcomponents/strings/pt-BR.json | 2 +- .../emby-webcomponents/strings/ru.json | 10 +- .../bower_components/fetch/.bower.json | 14 +- .../bower_components/fetch/bower.json | 3 - dashboard-ui/bower_components/fetch/fetch.js | 159 ++++++++------- .../components/navdrawer/navdrawer.js | 2 +- dashboard-ui/itemlist.html | 2 +- dashboard-ui/movies.html | 4 +- dashboard-ui/photos.html | 2 +- dashboard-ui/scripts/moviegenres.js | 116 ++++++++--- dashboard-ui/scripts/sections.js | 2 +- dashboard-ui/scripts/site.js | 1 + dashboard-ui/scripts/tvgenres.js | 116 ++++++++--- dashboard-ui/scripts/tvrecommended.js | 8 +- dashboard-ui/scripts/tvshows.js | 18 +- dashboard-ui/tv.html | 41 ++-- 20 files changed, 541 insertions(+), 380 deletions(-) create mode 100644 dashboard-ui/bower_components/emby-webcomponents/images/lazyloader.js diff --git a/dashboard-ui/bower_components/emby-webcomponents/.bower.json b/dashboard-ui/bower_components/emby-webcomponents/.bower.json index 82b069d140..d94075b70d 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/.bower.json +++ b/dashboard-ui/bower_components/emby-webcomponents/.bower.json @@ -14,12 +14,12 @@ }, "devDependencies": {}, "ignore": [], - "version": "1.4.336", - "_release": "1.4.336", + "version": "1.4.337", + "_release": "1.4.337", "_resolution": { "type": "version", - "tag": "1.4.336", - "commit": "400863fdd7ccf3fe53c1abd8b4d4dae58bd9925d" + "tag": "1.4.337", + "commit": "1653cc935acba06056afae3a6fc8bce17fb46a7a" }, "_source": "https://github.com/MediaBrowser/emby-webcomponents.git", "_target": "^1.2.1", diff --git a/dashboard-ui/bower_components/emby-webcomponents/images/imagehelper.js b/dashboard-ui/bower_components/emby-webcomponents/images/imagehelper.js index 28728978be..7e604ed3a0 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/images/imagehelper.js +++ b/dashboard-ui/bower_components/emby-webcomponents/images/imagehelper.js @@ -1,49 +1,12 @@ -define(['visibleinviewport', 'imageFetcher', 'layoutManager', 'events', 'browser', 'dom', 'appSettings', 'require'], function (visibleinviewport, imageFetcher, layoutManager, events, browser, dom, appSettings, require) { +define(['lazyLoader', 'imageFetcher', 'layoutManager', 'browser', 'appSettings', 'require'], function (lazyLoader, imageFetcher, layoutManager, browser, appSettings, require) { 'use strict'; - var thresholdX; - var thresholdY; - var requestIdleCallback = window.requestIdleCallback || function (fn) { fn(); }; //var imagesWorker = new Worker(require.toUrl('.').split('?')[0] + '/imagesworker.js'); - var supportsIntersectionObserver = function () { - - if (window.IntersectionObserver) { - - return true; - } - - return false; - }(); - - function resetThresholds() { - - var x = screen.availWidth; - var y = screen.availHeight; - - if (browser.touch) { - x *= 1.5; - y *= 1.5; - } - - thresholdX = x; - thresholdY = y; - } - - if (!supportsIntersectionObserver) { - dom.addEventListener(window, "orientationchange", resetThresholds, { passive: true }); - dom.addEventListener(window, 'resize', resetThresholds, { passive: true }); - resetThresholds(); - } - - function isVisible(elem) { - return visibleinviewport(elem, true, thresholdX, thresholdY); - } - var wheelEvent = (document.implementation.hasFeature('Event.wheel', '3.0') ? 'wheel' : 'mousewheel'); var self = {}; @@ -236,135 +199,9 @@ define(['visibleinviewport', 'imageFetcher', 'layoutManager', 'events', 'browser elem.animate(keyframes, timing); } - function cancelAll(tokens) { - for (var i = 0, length = tokens.length; i < length; i++) { - - tokens[i] = true; - } - } - - function unveilWithIntersection(images, root) { - - var filledCount = 0; - - var options = {}; - - //options.rootMargin = "300%"; - - var observer = new IntersectionObserver(function (entries) { - for (var j = 0, length2 = entries.length; j < length2; j++) { - var entry = entries[j]; - var target = entry.target; - observer.unobserve(target); - fillImage(target); - filledCount++; - } - }, - options - ); - // Start observing an element - for (var i = 0, length = images.length; i < length; i++) { - observer.observe(images[i]); - } - } - - function unveilElements(images, root) { - - if (!images.length) { - return; - } - - if (supportsIntersectionObserver) { - unveilWithIntersection(images, root); - return; - } - - var filledImages = []; - var cancellationTokens = []; - - function unveilInternal(tokenIndex) { - - var anyFound = false; - var out = false; - - // TODO: This out construct assumes left to right, top to bottom - - for (var i = 0, length = images.length; i < length; i++) { - - if (cancellationTokens[tokenIndex]) { - return; - } - if (filledImages[i]) { - continue; - } - var img = images[i]; - if (!out && isVisible(img)) { - anyFound = true; - filledImages[i] = true; - fillImage(img); - } else { - - if (anyFound) { - out = true; - } - } - } - - if (!images.length) { - dom.removeEventListener(document, 'focus', unveil, { - capture: true, - passive: true - }); - dom.removeEventListener(document, 'scroll', unveil, { - capture: true, - passive: true - }); - dom.removeEventListener(document, wheelEvent, unveil, { - capture: true, - passive: true - }); - dom.removeEventListener(window, 'resize', unveil, { - capture: true, - passive: true - }); - } - } - - function unveil() { - - cancelAll(cancellationTokens); - - var index = cancellationTokens.length; - cancellationTokens.length++; - - setTimeout(function () { - unveilInternal(index); - }, 1); - } - - dom.addEventListener(document, 'focus', unveil, { - capture: true, - passive: true - }); - dom.addEventListener(document, 'scroll', unveil, { - capture: true, - passive: true - }); - dom.addEventListener(document, wheelEvent, unveil, { - capture: true, - passive: true - }); - dom.addEventListener(window, 'resize', unveil, { - capture: true, - passive: true - }); - - unveil(); - } - function lazyChildren(elem) { - unveilElements(elem.getElementsByClassName('lazy'), elem); + lazyLoader.lazyChildren(elem, fillImage); } function getPrimaryImageAspectRatio(items) { diff --git a/dashboard-ui/bower_components/emby-webcomponents/images/lazyloader.js b/dashboard-ui/bower_components/emby-webcomponents/images/lazyloader.js new file mode 100644 index 0000000000..ed3f435a36 --- /dev/null +++ b/dashboard-ui/bower_components/emby-webcomponents/images/lazyloader.js @@ -0,0 +1,182 @@ +define(['visibleinviewport', 'browser', 'dom'], function (visibleinviewport, browser, dom) { + 'use strict'; + + var thresholdX; + var thresholdY; + + var requestIdleCallback = window.requestIdleCallback || function (fn) { + fn(); + }; + + var supportsIntersectionObserver = function () { + + if (window.IntersectionObserver) { + + return true; + } + + return false; + }(); + + function resetThresholds() { + + var x = screen.availWidth; + var y = screen.availHeight; + + if (browser.touch) { + x *= 1.5; + y *= 1.5; + } + + thresholdX = x; + thresholdY = y; + } + + if (!supportsIntersectionObserver) { + dom.addEventListener(window, "orientationchange", resetThresholds, { passive: true }); + dom.addEventListener(window, 'resize', resetThresholds, { passive: true }); + resetThresholds(); + } + + function isVisible(elem) { + return visibleinviewport(elem, true, thresholdX, thresholdY); + } + + var wheelEvent = (document.implementation.hasFeature('Event.wheel', '3.0') ? 'wheel' : 'mousewheel'); + var self = {}; + + function cancelAll(tokens) { + for (var i = 0, length = tokens.length; i < length; i++) { + + tokens[i] = true; + } + } + + function unveilWithIntersection(elements, root, callback) { + + var filledCount = 0; + + var options = {}; + + //options.rootMargin = "300%"; + + var observer = new IntersectionObserver(function (entries) { + for (var j = 0, length2 = entries.length; j < length2; j++) { + var entry = entries[j]; + var target = entry.target; + observer.unobserve(target); + callback(target); + filledCount++; + } + }, + options + ); + // Start observing an element + for (var i = 0, length = elements.length; i < length; i++) { + observer.observe(elements[i]); + } + } + + function unveilElements(elements, root, callback) { + + if (!elements.length) { + return; + } + + if (supportsIntersectionObserver) { + unveilWithIntersection(elements, root, callback); + return; + } + + var unveiledElements = []; + var cancellationTokens = []; + + function unveilInternal(tokenIndex) { + + var anyFound = false; + var out = false; + + // TODO: This out construct assumes left to right, top to bottom + + for (var i = 0, length = elements.length; i < length; i++) { + + if (cancellationTokens[tokenIndex]) { + return; + } + if (unveiledElements[i]) { + continue; + } + var elem = elements[i]; + if (!out && isVisible(elem)) { + anyFound = true; + unveiledElements[i] = true; + callback(elem); + } else { + + if (anyFound) { + out = true; + } + } + } + + if (!elements.length) { + dom.removeEventListener(document, 'focus', unveil, { + capture: true, + passive: true + }); + dom.removeEventListener(document, 'scroll', unveil, { + capture: true, + passive: true + }); + dom.removeEventListener(document, wheelEvent, unveil, { + capture: true, + passive: true + }); + dom.removeEventListener(window, 'resize', unveil, { + capture: true, + passive: true + }); + } + } + + function unveil() { + + cancelAll(cancellationTokens); + + var index = cancellationTokens.length; + cancellationTokens.length++; + + setTimeout(function () { + unveilInternal(index); + }, 1); + } + + dom.addEventListener(document, 'focus', unveil, { + capture: true, + passive: true + }); + dom.addEventListener(document, 'scroll', unveil, { + capture: true, + passive: true + }); + dom.addEventListener(document, wheelEvent, unveil, { + capture: true, + passive: true + }); + dom.addEventListener(window, 'resize', unveil, { + capture: true, + passive: true + }); + + unveil(); + } + + function lazyChildren(elem, callback) { + + unveilElements(elem.getElementsByClassName('lazy'), elem, callback); + } + + self.lazyChildren = lazyChildren; + + return self; +}); \ 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 20e7ee5a37..8af5400073 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/strings/nl.json +++ b/dashboard-ui/bower_components/emby-webcomponents/strings/nl.json @@ -1,7 +1,7 @@ { - "MessageUnlockAppWithPurchaseOrSupporter": "Unlock this feature with a small one-time purchase, or with an active Emby Premiere subscription.", - "MessageUnlockAppWithSupporter": "Unlock this feature with an active Emby Premiere subscription.", - "MessageToValidateSupporter": "If you have an active Emby Premiere subscription, ensure you've setup Emby Premiere in your Emby Server Dashboard, which you can access by clicking Emby Premiere within the main menu.", + "MessageUnlockAppWithPurchaseOrSupporter": "Ontgrendel deze functie met een kleine eenmalige aankoop, of met een actief Emby Premiere abonnement.", + "MessageUnlockAppWithSupporter": "Ontgrendel deze functie met een actief Emby Premiere abonnement.", + "MessageToValidateSupporter": "Als u een actieve Emby Premiere abonnement heeft , zorg er dan voor dat u deze activeert in uw Emby Server Dashboard door te klikken op Emby Premiere in het hoofdmenu.", "ValueSpecialEpisodeName": "Speciaal - {0}", "Share": "Delen", "Add": "Toevoegen", @@ -29,7 +29,7 @@ "ButtonGotIt": "Begrepen", "ButtonRestart": "Herstart", "RecordingCancelled": "Opname geannuleerd.", - "SeriesCancelled": "Series cancelled.", + "SeriesCancelled": "Serie geannuleerd.", "RecordingScheduled": "Opname schema", "SeriesRecordingScheduled": "Serieopname gepland.", "HeaderNewRecording": "Nieuwe opname", @@ -42,19 +42,19 @@ "Saturday": "Zaterdag", "Days": "Dagen", "RecordSeries": "Series Opnemen", - "HeaderCinemaMode": "Cinema Mode", - "HeaderCloudSync": "Cloud Sync", + "HeaderCinemaMode": "Bioscoop mode", + "HeaderCloudSync": "Cloud Synchronisatie", "HeaderOfflineDownloads": "Offline Media", - "HeaderOfflineDownloadsDescription": "Download media to your devices for easy offline use.", - "CloudSyncFeatureDescription": "Sync your media to the cloud for easy backup, archiving, and converting.", - "CoverArtFeatureDescription": "Cover Art creates fun covers and other treatments to help you personalize your media images.", + "HeaderOfflineDownloadsDescription": "Download media naar je apparaten voor gemakkelijk offlineebruik.", + "CloudSyncFeatureDescription": "Synchroniseer uw media naar de cloud voor eenvoudige backup, archivering en conversie.", + "CoverArtFeatureDescription": "Cover Art cre\u00ebert leuke covers en andere bewerkingen om u te helpen uw mediabeelden te personaliseren.", "CoverArt": "Cover Art", - "CinemaModeFeatureDescription": "Cinema Mode gives you the true cinema experience with trailers and custom intros before the feature.", - "HeaderFreeApps": "Free Emby Apps", - "FreeAppsFeatureDescription": "Enjoy free access to Emby apps for your devices.", + "CinemaModeFeatureDescription": "Bioscoop mode geeft u de ware bioscoopervaring met trailers en aangepaste intro voor de weergave van uw keuze.", + "HeaderFreeApps": "Gratis Emby Apps", + "FreeAppsFeatureDescription": "Geniet van gratis toegang tot Emby apps voor uw apparaten.", "HeaderBecomeProjectSupporter": "Verkrijg Emby Premiere", "MessageActiveSubscriptionRequiredSeriesRecordings": "Er is een actief Emby Premiere abonnement benodigd om een automatische serie opname aan te maken.", - "LabelEmailAddress": "E-mail address:", + "LabelEmailAddress": "E-mailadres:", "PromoConvertRecordingsToStreamingFormat": "Automatisch converteren opnames naar een streaming formaat met Emby Premiere. Opnames zullen on the fly worden omgezet naar MP4 of MKV, op basis van deEmby server instellingen.", "FeatureRequiresEmbyPremiere": "Deze functie vereist een actieve Emby Premiere abonnement.", "HeaderConvertYourRecordings": "Opnames omzetten", @@ -293,31 +293,31 @@ "RepeatEpisodes": "Herhaal afleveringen", "DvrSubscriptionRequired": "Emby DVR vereist een actieve Emby premi\u00e8re-abonnement.", "HeaderCancelRecording": "Opname Annuleren", - "CancelRecording": "Cancel recording", + "CancelRecording": "Opname annuleren", "HeaderKeepRecording": "Bewaar opname", - "HeaderCancelSeries": "Cancel Series", - "HeaderKeepSeries": "Keep Series", + "HeaderCancelSeries": "Annuleren Series", + "HeaderKeepSeries": "Series behouden", "HeaderLearnMore": "Meer informatie", "DeleteMedia": "Verwijder media", "SeriesSettings": "Series instellingen", "HeaderRecordingOptions": "Opname instellingen", "CancelSeries": "Cancel series", - "DoNotRecord": "Do not record", - "HeaderSeriesOptions": "Series Options", - "LabelChannels": "Channels:", - "ChannelNameOnly": "Channel {0} only", - "Anytime": "Anytime", - "AroundTime": "Around {0}", - "LabelAirtime": "Airtime:", - "AllChannels": "All channels", - "LabelRecord": "Record:", - "NewEpisodesOnly": "New episodes only", - "AllEpisodes": "All episodes", - "LabelStartWhenPossible": "Start when possible:", - "LabelStopWhenPossible": "Stop when possible:", - "MinutesBefore": "minutes before", - "MinutesAfter": "minutes after", - "SkipEpisodesAlreadyInMyLibrary": "Skip episodes that are already in my library", + "DoNotRecord": "Niet opnemen", + "HeaderSeriesOptions": "Series Opties", + "LabelChannels": "Kanalen:", + "ChannelNameOnly": "Alleen kanaal {0}", + "Anytime": "Op elk moment", + "AroundTime": "Rond {0}", + "LabelAirtime": "Uitzendtijd:", + "AllChannels": "Alle kanalen", + "LabelRecord": "Opnemen:", + "NewEpisodesOnly": "Alleen nieuwe afleveringen", + "AllEpisodes": "Alle afleveringen", + "LabelStartWhenPossible": "Start indien mogelijk:", + "LabelStopWhenPossible": "Stop indien mogelijk:", + "MinutesBefore": "minuten voor", + "MinutesAfter": "minuten na", + "SkipEpisodesAlreadyInMyLibrary": "Sla afleveringen over die al in mijn bibliotheek voorkomen", "SkipEpisodesAlreadyInMyLibraryHelp": "Episodes will be compared using season and episode numbers, when available.", "LabelKeepUpTo": "Keep up to:", "AsManyAsPossible": "As many as possible", 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 cbe24b0cbb..0823010e6c 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/strings/pt-BR.json +++ b/dashboard-ui/bower_components/emby-webcomponents/strings/pt-BR.json @@ -236,7 +236,7 @@ "PackageInstallCompleted": "Instala\u00e7\u00e3o de {0} conclu\u00edda.", "PackageInstallFailed": "Instala\u00e7\u00e3o de {0} falhou.", "PackageInstallCancelled": "Instala\u00e7\u00e3o de {0} cancelada.", - "SeriesYearToPresent": "{0}-Presente", + "SeriesYearToPresent": "{0} - Presente", "ValueOneSong": "1 m\u00fasica", "ValueSongCount": "{0} m\u00fasicas", "ValueOneMovie": "1 filme", diff --git a/dashboard-ui/bower_components/emby-webcomponents/strings/ru.json b/dashboard-ui/bower_components/emby-webcomponents/strings/ru.json index 7e4e9b9fcf..cc2418518e 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/strings/ru.json +++ b/dashboard-ui/bower_components/emby-webcomponents/strings/ru.json @@ -1,5 +1,5 @@ { - "MessageUnlockAppWithPurchaseOrSupporter": "\u0420\u0430\u0437\u0431\u043b\u043e\u043a\u0438\u0440\u0443\u0439\u0442\u0435 \u0434\u0430\u043d\u043d\u044b\u0439 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u043e\u043c \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u043e\u0434\u043d\u043e\u0440\u0430\u0437\u043e\u0432\u043e\u0439 \u043e\u043f\u043b\u0430\u0442\u044b, \u0438\u043b\u0438 \u0441 \u0434\u0435\u0439\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0439 \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u043e\u0439 Emby Premiere .", + "MessageUnlockAppWithPurchaseOrSupporter": "\u0420\u0430\u0437\u0431\u043b\u043e\u043a\u0438\u0440\u0443\u0439\u0442\u0435 \u0434\u0430\u043d\u043d\u044b\u0439 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u043e\u043c \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u043e\u0434\u043d\u043e\u043a\u0440\u0430\u0442\u043d\u043e\u0439 \u043e\u043f\u043b\u0430\u0442\u044b, \u0438\u043b\u0438 \u0441 \u0434\u0435\u0439\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0439 \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u043e\u0439 Emby Premiere .", "MessageUnlockAppWithSupporter": "\u0420\u0430\u0437\u0431\u043b\u043e\u043a\u0438\u0440\u0443\u0439\u0442\u0435 \u0434\u0430\u043d\u043d\u044b\u0439 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u0441 \u0434\u0435\u0439\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0439 \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u043e\u0439 Emby Premiere.", "MessageToValidateSupporter": "\u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u0438\u043c\u0435\u0435\u0442\u0441\u044f \u0434\u0435\u0439\u0441\u0442\u0432\u0443\u044e\u0449\u0430\u044f \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0430 Emby Premiere, \u0443\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e Emby Premiere \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0430 \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u0430 \u0432 \u0432\u0430\u0448\u0435\u0439 \u041f\u0430\u043d\u0435\u043b\u0438 Emby Server, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0430 \u043f\u043e \u0449\u0435\u043b\u0447\u043a\u0443 \u043f\u043e Emby Premiere \u0432 \u0433\u043b\u0430\u0432\u043d\u043e\u043c \u043c\u0435\u043d\u044e.", "ValueSpecialEpisodeName": "\u0421\u043f\u0435\u0446\u044d\u043f\u0438\u0437\u043e\u0434 - {0}", @@ -53,7 +53,7 @@ "HeaderFreeApps": "\u0411\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u044b\u0435 Emby-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f", "FreeAppsFeatureDescription": "\u0412\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435\u0441\u044c \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u044b\u043c \u0434\u043e\u0441\u0442\u0443\u043f\u043e\u043c \u043a Emby-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f\u043c \u0434\u043b\u044f \u0432\u0430\u0448\u0438\u0445 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432.", "HeaderBecomeProjectSupporter": "\u041f\u0440\u0438\u043e\u0431\u0440\u0435\u0441\u0442\u0438 Emby Premiere", - "MessageActiveSubscriptionRequiredSeriesRecordings": "\u0414\u0435\u0439\u0441\u0442\u0432\u0443\u044e\u0449\u0430\u044f \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0430 Emby Premiere \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0437\u0430\u043f\u0438\u0441\u0438 \u0441\u0435\u0440\u0438\u0430\u043b\u043e\u0432.", + "MessageActiveSubscriptionRequiredSeriesRecordings": "\u0414\u0435\u0439\u0441\u0442\u0432\u0443\u044e\u0449\u0430\u044f \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0430 Emby Premiere \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0441\u0435\u0440\u0438\u0439\u043d\u044b\u0435 \u0437\u0430\u043f\u0438\u0441\u0438.", "LabelEmailAddress": "\u0410\u0434\u0440\u0435\u0441 \u042d-\u043f\u043e\u0447\u0442\u044b:", "PromoConvertRecordingsToStreamingFormat": "\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0437\u0430\u043f\u0438\u0441\u0435\u0439 \u0432 \u0443\u0434\u043e\u0431\u043d\u044b\u0439 \u0434\u043b\u044f \u0442\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u0438 \u0444\u043e\u0440\u043c\u0430\u0442 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Emby Premiere. \u0417\u0430\u043f\u0438\u0441\u0438 \u0431\u0443\u0434\u0443\u0442 \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u0432 MP4 \u0438\u043b\u0438 MKV, \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 Emby Server.", "FeatureRequiresEmbyPremiere": "\u0414\u043b\u044f \u0434\u0430\u043d\u043d\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0434\u0435\u0439\u0441\u0442\u0432\u0443\u044e\u0449\u0430\u044f \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0430 Emby Premiere.", @@ -146,7 +146,7 @@ "LabelSortTitle": "\u0421\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0430 \u043f\u043e \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044e:", "LabelDateAdded": "\u0414\u0430\u0442\u0430 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f:", "ConfigureDateAdded": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0434\u0430\u0442\u044b \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442\u0441\u044f \u0432 \u041f\u0430\u043d\u0435\u043b\u0438 Emby Server \u0432 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u0445 \u041c\u0435\u0434\u0438\u0430\u0442\u0435\u043a\u0438", - "LabelStatus": "\u0421\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435:", + "LabelStatus": "\u0421\u0442\u0430\u0442\u0443\u0441:", "LabelArtists": "\u0418\u0441\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u0438:", "LabelArtistsHelp": "\u0414\u043b\u044f \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u00ab;\u00bb", "LabelAlbumArtists": "\u0418\u0441\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u0438 \u0430\u043b\u044c\u0431\u043e\u043c\u0430:", @@ -256,7 +256,7 @@ "PleaseEnterNameOrId": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0438\u043b\u0438 \u0432\u043d\u0435\u0448\u043d\u0438\u0439 ID.", "MessageItemSaved": "\u042d\u043b\u0435\u043c\u0435\u043d\u0442 \u0441\u043e\u0445\u0440\u0430\u043d\u0451\u043d.", "SearchResults": "\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u043f\u043e\u0438\u0441\u043a\u0430", - "SyncToOtherDevice": "\u0421\u0438\u043d\u0445\u0440\u043e \u0441 \u0434\u0440\u0443\u0433\u0438\u043c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u043c", + "SyncToOtherDevice": "\u0421\u0438\u043d\u0445\u0440\u043e \u0441 \u0434\u0440. \u0443\u0441\u0442\u0440-\u043e\u043c", "MakeAvailableOffline": "\u0421\u0434\u0435\u043b\u0430\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u043c \u0430\u0432\u0442\u043e\u043d\u043e\u043c\u043d\u043e", "ServerNameIsRestarting": "Emby Server - {0} \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0441\u044f.", "ServerNameIsShuttingDown": "Emby Server - {0} \u0437\u0430\u0432\u0435\u0440\u0448\u0430\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0443.", @@ -291,7 +291,7 @@ "LiveBroadcasts": "\u041f\u0440\u044f\u043c\u044b\u0435 \u0442\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u0438", "Premieres": "\u041f\u0440\u0435\u043c\u044c\u0435\u0440\u044b", "RepeatEpisodes": "\u041f\u043e\u0432\u0442\u043e\u0440 \u044d\u043f\u0438\u0437\u043e\u0434\u043e\u0432", - "DvrSubscriptionRequired": "\u0414\u043b\u044f Emby DVR \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0434\u0435\u0439\u0441\u0442\u0432\u0443\u044e\u0449\u0430\u044f \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0430 Emby Premiere.", + "DvrSubscriptionRequired": "\u0414\u043b\u044f \u0432\u0438\u0434\u0435\u043e\u0440\u0435\u043a\u043e\u0440\u0434\u0435\u0440\u0430 Emby \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0434\u0435\u0439\u0441\u0442\u0432\u0443\u044e\u0449\u0430\u044f \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0430 Emby Premiere.", "HeaderCancelRecording": "\u041e\u0442\u043c\u0435\u043d\u0438\u0442\u044c \u0437\u0430\u043f\u0438\u0441\u044c", "CancelRecording": "\u041e\u0442\u043c\u0435\u043d\u0438\u0442\u044c \u0437\u0430\u043f\u0438\u0441\u044c", "HeaderKeepRecording": "\u041f\u0440\u043e\u0434\u043e\u043b\u0436\u0435\u043d\u0438\u0435 \u0437\u0430\u043f\u0438\u0441\u0438", diff --git a/dashboard-ui/bower_components/fetch/.bower.json b/dashboard-ui/bower_components/fetch/.bower.json index 36c9a6fdea..12f9f791a8 100644 --- a/dashboard-ui/bower_components/fetch/.bower.json +++ b/dashboard-ui/bower_components/fetch/.bower.json @@ -1,9 +1,6 @@ { "name": "fetch", "main": "fetch.js", - "devDependencies": { - "es6-promise": "1.0.0" - }, "ignore": [ ".*", "*.md", @@ -14,15 +11,14 @@ "test/" ], "homepage": "https://github.com/github/fetch", - "version": "1.0.0", - "_release": "1.0.0", + "version": "1.1.0", + "_release": "1.1.0", "_resolution": { "type": "version", - "tag": "v1.0.0", - "commit": "f054e7b5ce2bf7f86c8d7212c2de026800725b84" + "tag": "v1.1.0", + "commit": "76f6a09cbfc1c955479dd9da2a333f7404c79de2" }, "_source": "https://github.com/github/fetch.git", "_target": "^1.0.0", - "_originalSource": "fetch", - "_direct": true + "_originalSource": "fetch" } \ No newline at end of file diff --git a/dashboard-ui/bower_components/fetch/bower.json b/dashboard-ui/bower_components/fetch/bower.json index 8966a30894..266f12dc85 100644 --- a/dashboard-ui/bower_components/fetch/bower.json +++ b/dashboard-ui/bower_components/fetch/bower.json @@ -1,9 +1,6 @@ { "name": "fetch", "main": "fetch.js", - "devDependencies": { - "es6-promise": "1.0.0" - }, "ignore": [ ".*", "*.md", diff --git a/dashboard-ui/bower_components/fetch/fetch.js b/dashboard-ui/bower_components/fetch/fetch.js index d0652dea62..29c985942f 100644 --- a/dashboard-ui/bower_components/fetch/fetch.js +++ b/dashboard-ui/bower_components/fetch/fetch.js @@ -20,6 +20,28 @@ arrayBuffer: 'ArrayBuffer' in self } + if (support.arrayBuffer) { + var viewClasses = [ + '[object Int8Array]', + '[object Uint8Array]', + '[object Uint8ClampedArray]', + '[object Int16Array]', + '[object Uint16Array]', + '[object Int32Array]', + '[object Uint32Array]', + '[object Float32Array]', + '[object Float64Array]' + ] + + var isDataView = function(obj) { + return obj && DataView.prototype.isPrototypeOf(obj) + } + + var isArrayBufferView = ArrayBuffer.isView || function(obj) { + return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1 + } + } + function normalizeName(name) { if (typeof name !== 'string') { name = String(name) @@ -152,14 +174,26 @@ function readBlobAsArrayBuffer(blob) { var reader = new FileReader() + var promise = fileReaderReady(reader) reader.readAsArrayBuffer(blob) - return fileReaderReady(reader) + return promise } function readBlobAsText(blob) { var reader = new FileReader() + var promise = fileReaderReady(reader) reader.readAsText(blob) - return fileReaderReady(reader) + return promise + } + + function bufferClone(buf) { + if (buf.slice) { + return buf.slice(0) + } else { + var view = new Uint8Array(buf.byteLength) + view.set(new Uint8Array(buf)) + return view.buffer + } } function Body() { @@ -167,7 +201,9 @@ this._initBody = function(body) { this._bodyInit = body - if (typeof body === 'string') { + if (!body) { + this._bodyText = '' + } else if (typeof body === 'string') { this._bodyText = body } else if (support.blob && Blob.prototype.isPrototypeOf(body)) { this._bodyBlob = body @@ -175,11 +211,12 @@ this._bodyFormData = body } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { this._bodyText = body.toString() - } else if (!body) { - this._bodyText = '' - } else if (support.arrayBuffer && ArrayBuffer.prototype.isPrototypeOf(body)) { - // Only support ArrayBuffers for POST method. - // Receiving ArrayBuffers happens via Blobs, instead. + } else if (support.arrayBuffer && support.blob && isDataView(body)) { + this._bodyArrayBuffer = bufferClone(body.buffer) + // IE 10-11 can't handle a DataView body. + this._bodyInit = new Blob([this._bodyArrayBuffer]) + } else if (support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) { + this._bodyArrayBuffer = bufferClone(body) } else { throw new Error('unsupported BodyInit type') } @@ -204,36 +241,43 @@ if (this._bodyBlob) { return Promise.resolve(this._bodyBlob) + } else if (this._bodyArrayBuffer) { + return Promise.resolve(new Blob([this._bodyArrayBuffer])) } else if (this._bodyFormData) { throw new Error('could not read FormData body as blob') } else { return Promise.resolve(new Blob([this._bodyText])) } } + } + this.text = function() { + var rejected = consumed(this) + if (rejected) { + return rejected + } + + if (this._bodyBlob) { + return readBlobAsText(this._bodyBlob) + } else if (this._bodyArrayBuffer) { + var view = new Uint8Array(this._bodyArrayBuffer) + var str = String.fromCharCode.apply(null, view) + return Promise.resolve(str) + } else if (this._bodyFormData) { + throw new Error('could not read FormData body as text') + } else { + return Promise.resolve(this._bodyText) + } + } + + if (support.arrayBuffer) { this.arrayBuffer = function() { - return this.blob().then(readBlobAsArrayBuffer) - } - - this.text = function() { - var rejected = consumed(this) - if (rejected) { - return rejected - } - - if (this._bodyBlob) { - return readBlobAsText(this._bodyBlob) - } else if (this._bodyFormData) { - throw new Error('could not read FormData body as text') + if (this._bodyArrayBuffer) { + return consumed(this) || Promise.resolve(this._bodyArrayBuffer) } else { - return Promise.resolve(this._bodyText) + return this.blob().then(readBlobAsArrayBuffer) } } - } else { - this.text = function() { - var rejected = consumed(this) - return rejected ? rejected : Promise.resolve(this._bodyText) - } } if (support.formData) { @@ -260,7 +304,10 @@ function Request(input, options) { options = options || {} var body = options.body - if (Request.prototype.isPrototypeOf(input)) { + + if (typeof input === 'string') { + this.url = input + } else { if (input.bodyUsed) { throw new TypeError('Already read') } @@ -271,12 +318,10 @@ } this.method = input.method this.mode = input.mode - if (!body) { + if (!body && input._bodyInit != null) { body = input._bodyInit input.bodyUsed = true } - } else { - this.url = input } this.credentials = options.credentials || this.credentials || 'omit' @@ -294,7 +339,7 @@ } Request.prototype.clone = function() { - return new Request(this) + return new Request(this, { body: this._bodyInit }) } function decode(body) { @@ -310,16 +355,17 @@ return form } - function headers(xhr) { - var head = new Headers() - var pairs = (xhr.getAllResponseHeaders() || '').trim().split('\n') - pairs.forEach(function(header) { - var split = header.trim().split(':') - var key = split.shift().trim() - var value = split.join(':').trim() - head.append(key, value) + function parseHeaders(rawHeaders) { + var headers = new Headers() + rawHeaders.split('\r\n').forEach(function(line) { + var parts = line.split(':') + var key = parts.shift().trim() + if (key) { + var value = parts.join(':').trim() + headers.append(key, value) + } }) - return head + return headers } Body.call(Request.prototype) @@ -330,10 +376,10 @@ } this.type = 'default' - this.status = options.status + this.status = 'status' in options ? options.status : 200 this.ok = this.status >= 200 && this.status < 300 - this.statusText = options.statusText - this.headers = options.headers instanceof Headers ? options.headers : new Headers(options.headers) + this.statusText = 'statusText' in options ? options.statusText : 'OK' + this.headers = new Headers(options.headers) this.url = options.url || '' this._initBody(bodyInit) } @@ -371,35 +417,16 @@ self.fetch = function(input, init) { return new Promise(function(resolve, reject) { - var request - if (Request.prototype.isPrototypeOf(input) && !init) { - request = input - } else { - request = new Request(input, init) - } - + var request = new Request(input, init) var xhr = new XMLHttpRequest() - function responseURL() { - if ('responseURL' in xhr) { - return xhr.responseURL - } - - // Avoid security warnings on getResponseHeader when not allowed by CORS - if (/^X-Request-URL:/m.test(xhr.getAllResponseHeaders())) { - return xhr.getResponseHeader('X-Request-URL') - } - - return - } - xhr.onload = function() { var options = { status: xhr.status, statusText: xhr.statusText, - headers: headers(xhr), - url: responseURL() + headers: parseHeaders(xhr.getAllResponseHeaders() || '') } + options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL') var body = 'response' in xhr ? xhr.response : xhr.responseText resolve(new Response(body, options)) } diff --git a/dashboard-ui/components/navdrawer/navdrawer.js b/dashboard-ui/components/navdrawer/navdrawer.js index 21790dc597..1b5e6d6828 100644 --- a/dashboard-ui/components/navdrawer/navdrawer.js +++ b/dashboard-ui/components/navdrawer/navdrawer.js @@ -97,7 +97,7 @@ if (options.disableEdgeSwipe) { return; } - + return; require(['hammer-main'], initEdgeSwipeInternal); } diff --git a/dashboard-ui/itemlist.html b/dashboard-ui/itemlist.html index c52644e24d..dfcff645d4 100644 --- a/dashboard-ui/itemlist.html +++ b/dashboard-ui/itemlist.html @@ -1,4 +1,4 @@ -
+
diff --git a/dashboard-ui/movies.html b/dashboard-ui/movies.html index ebc4a979fa..a7246720b0 100644 --- a/dashboard-ui/movies.html +++ b/dashboard-ui/movies.html @@ -105,10 +105,10 @@
-
+
-
+
diff --git a/dashboard-ui/photos.html b/dashboard-ui/photos.html index 2805a7f5b3..b6598698aa 100644 --- a/dashboard-ui/photos.html +++ b/dashboard-ui/photos.html @@ -1,4 +1,4 @@ -
+
diff --git a/dashboard-ui/scripts/moviegenres.js b/dashboard-ui/scripts/moviegenres.js index df7008954c..1aa3199e25 100644 --- a/dashboard-ui/scripts/moviegenres.js +++ b/dashboard-ui/scripts/moviegenres.js @@ -1,4 +1,4 @@ -define(['libraryBrowser', 'cardBuilder'], function (libraryBrowser, cardBuilder) { +define(['libraryBrowser', 'cardBuilder', 'lazyLoader', 'apphost'], function (libraryBrowser, cardBuilder, lazyLoader, appHost) { 'use strict'; return function (view, params, tabContent) { @@ -17,10 +17,9 @@ SortOrder: "Ascending", IncludeItemTypes: "Movie", Recursive: true, - Fields: "DateCreated,ItemCounts,PrimaryImageAspectRatio", - StartIndex: 0 + EnableTotalRecordCount: false }, - view: libraryBrowser.getSavedView(key) || 'Thumb' + view: libraryBrowser.getSavedView(key) || 'PosterCard' }; pageData.query.ParentId = params.topParentId; @@ -36,7 +35,7 @@ function getSavedQueryKey() { - return libraryBrowser.getSavedQueryKey('genres'); + return libraryBrowser.getSavedQueryKey('moviegenres'); } function getPromise() { @@ -47,65 +46,136 @@ return ApiClient.getGenres(Dashboard.getCurrentUserId(), query); } - function reloadItems(context, promise) { + function enableScrollX() { + return browserInfo.mobile && AppInfo.enableAppLayouts; + } - var query = getQuery(); + function getThumbShape() { + return enableScrollX() ? 'overflowBackdrop' : 'backdrop'; + } - promise.then(function (result) { + function getPortraitShape() { + return enableScrollX() ? 'overflowPortrait' : 'portrait'; + } + + function fillItemsContainer(elem) { - var html = ''; + var id = elem.getAttribute('data-id'); - var viewStyle = self.getCurrentViewStyle(); - var elem = context.querySelector('#items'); + var viewStyle = self.getCurrentViewStyle(); + + var limit = viewStyle == 'Thumb' || viewStyle == 'ThumbCard' ? + 5 : + 8; + + var enableImageTypes = viewStyle == 'Thumb' || viewStyle == 'ThumbCard' ? + "Primary,Backdrop,Thumb" : + "Primary"; + + var query = { + SortBy: "SortName", + SortOrder: "Ascending", + IncludeItemTypes: "Movie", + Recursive: true, + Fields: "PrimaryImageAspectRatio,MediaSourceCount,BasicSyncInfo", + ImageTypeLimit: 1, + EnableImageTypes: enableImageTypes, + Limit: limit, + GenreIds: id, + EnableTotalRecordCount: false + }; + + ApiClient.getItems(Dashboard.getCurrentUserId(), query).then(function (result) { + + var supportsImageAnalysis = appHost.supports('imageanalysis'); if (viewStyle == "Thumb") { cardBuilder.buildCards(result.Items, { itemsContainer: elem, - shape: "backdrop", + shape: getThumbShape(), preferThumb: true, showTitle: true, scalable: true, - showItemCounts: true, centerText: true, - overlayMoreButton: true + overlayMoreButton: true, + allowBottomPadding: false }); } else if (viewStyle == "ThumbCard") { cardBuilder.buildCards(result.Items, { itemsContainer: elem, - shape: "backdrop", + shape: getThumbShape(), preferThumb: true, showTitle: true, scalable: true, - showItemCounts: true, centerText: false, - cardLayout: true + cardLayout: true, + vibrant: supportsImageAnalysis, + showYear: true }); } else if (viewStyle == "PosterCard") { cardBuilder.buildCards(result.Items, { itemsContainer: elem, - shape: "auto", + shape: getPortraitShape(), showTitle: true, scalable: true, - showItemCounts: true, centerText: false, - cardLayout: true + cardLayout: true, + vibrant: supportsImageAnalysis, + showYear: true }); } else if (viewStyle == "Poster") { cardBuilder.buildCards(result.Items, { itemsContainer: elem, - shape: "auto", + shape: getPortraitShape(), showTitle: true, scalable: true, - showItemCounts: true, centerText: true, - overlayMoreButton: true + overlayMoreButton: true, + allowBottomPadding: false }); } + }); + } + + function reloadItems(context, promise) { + + var query = getQuery(); + + promise.then(function (result) { + + var elem = context.querySelector('#items'); + var html = ''; + + var items = result.Items; + + for (var i = 0, length = items.length; i < length; i++) { + + var item = items[i]; + + html += '
'; + html += '

'; + html += item.Name; + html += '

'; + + if (enableScrollX()) { + html += '
'; + } else { + html += '
'; + } + html += '
'; + + html += '
'; + } + + elem.innerHTML = html; + + lazyLoader.lazyChildren(elem, fillItemsContainer); + libraryBrowser.saveQueryValues(getSavedQueryKey(), query); Dashboard.hideLoadingMsg(); diff --git a/dashboard-ui/scripts/sections.js b/dashboard-ui/scripts/sections.js index e80e917df0..dcea2f867f 100644 --- a/dashboard-ui/scripts/sections.js +++ b/dashboard-ui/scripts/sections.js @@ -299,7 +299,7 @@ showUnplayedIndicator: false, showChildCountIndicator: true, context: 'home', - overlayText: !cardLayout, + overlayText: false, centerText: !cardLayout, overlayPlayButton: viewType !== 'photos', allowBottomPadding: !enableScrollX() && !cardLayout, diff --git a/dashboard-ui/scripts/site.js b/dashboard-ui/scripts/site.js index 3ee9d426cd..a4dd21af98 100644 --- a/dashboard-ui/scripts/site.js +++ b/dashboard-ui/scripts/site.js @@ -1222,6 +1222,7 @@ var AppInfo = {}; define("libjass", [bowerPath + "/libjass/libjass.min", "css!" + bowerPath + "/libjass/libjass"], returnFirstDependency); + define("lazyLoader", [embyWebComponentsBowerPath + "/images/lazyloader"], returnFirstDependency); define("imageLoader", [embyWebComponentsBowerPath + "/images/imagehelper"], returnFirstDependency); define("syncJobList", ["components/syncjoblist/syncjoblist"], returnFirstDependency); define("appfooter", ["components/appfooter/appfooter"], returnFirstDependency); diff --git a/dashboard-ui/scripts/tvgenres.js b/dashboard-ui/scripts/tvgenres.js index 070618c677..0b3af0674e 100644 --- a/dashboard-ui/scripts/tvgenres.js +++ b/dashboard-ui/scripts/tvgenres.js @@ -1,4 +1,4 @@ -define(['libraryBrowser', 'cardBuilder'], function (libraryBrowser, cardBuilder) { +define(['libraryBrowser', 'cardBuilder', 'lazyLoader', 'apphost'], function (libraryBrowser, cardBuilder, lazyLoader, appHost) { 'use strict'; return function (view, params, tabContent) { @@ -17,10 +17,9 @@ SortOrder: "Ascending", IncludeItemTypes: "Series", Recursive: true, - Fields: "DateCreated,ItemCounts,PrimaryImageAspectRatio", - StartIndex: 0 + EnableTotalRecordCount: false }, - view: libraryBrowser.getSavedView(key) || 'Thumb' + view: libraryBrowser.getSavedView(key) || 'PosterCard' }; pageData.query.ParentId = params.topParentId; @@ -36,7 +35,7 @@ function getSavedQueryKey() { - return libraryBrowser.getSavedQueryKey('genres'); + return libraryBrowser.getSavedQueryKey('seriesgenres'); } function getPromise() { @@ -47,65 +46,136 @@ return ApiClient.getGenres(Dashboard.getCurrentUserId(), query); } - function reloadItems(context, promise) { + function enableScrollX() { + return browserInfo.mobile && AppInfo.enableAppLayouts; + } - var query = getQuery(); + function getThumbShape() { + return enableScrollX() ? 'overflowBackdrop' : 'backdrop'; + } - promise.then(function (result) { + function getPortraitShape() { + return enableScrollX() ? 'overflowPortrait' : 'portrait'; + } + + function fillItemsContainer(elem) { - var html = ''; + var id = elem.getAttribute('data-id'); - var viewStyle = self.getCurrentViewStyle(); - var elem = context.querySelector('#items'); + var viewStyle = self.getCurrentViewStyle(); + + var limit = viewStyle == 'Thumb' || viewStyle == 'ThumbCard' ? + 5 : + 8; + + var enableImageTypes = viewStyle == 'Thumb' || viewStyle == 'ThumbCard' ? + "Primary,Backdrop,Thumb" : + "Primary"; + + var query = { + SortBy: "SortName", + SortOrder: "Ascending", + IncludeItemTypes: "Series", + Recursive: true, + Fields: "PrimaryImageAspectRatio,MediaSourceCount,BasicSyncInfo", + ImageTypeLimit: 1, + EnableImageTypes: enableImageTypes, + Limit: limit, + GenreIds: id, + EnableTotalRecordCount: false + }; + + ApiClient.getItems(Dashboard.getCurrentUserId(), query).then(function (result) { + + var supportsImageAnalysis = appHost.supports('imageanalysis'); if (viewStyle == "Thumb") { cardBuilder.buildCards(result.Items, { itemsContainer: elem, - shape: "backdrop", + shape: getThumbShape(), preferThumb: true, showTitle: true, scalable: true, - showItemCounts: true, centerText: true, - overlayMoreButton: true + overlayMoreButton: true, + allowBottomPadding: false }); } else if (viewStyle == "ThumbCard") { cardBuilder.buildCards(result.Items, { itemsContainer: elem, - shape: "backdrop", + shape: getThumbShape(), preferThumb: true, showTitle: true, scalable: true, - showItemCounts: true, centerText: false, - cardLayout: true + cardLayout: true, + vibrant: supportsImageAnalysis, + showYear: true }); } else if (viewStyle == "PosterCard") { cardBuilder.buildCards(result.Items, { itemsContainer: elem, - shape: "auto", + shape: getPortraitShape(), showTitle: true, scalable: true, - showItemCounts: true, centerText: false, - cardLayout: true + cardLayout: true, + vibrant: supportsImageAnalysis, + showYear: true }); } else if (viewStyle == "Poster") { cardBuilder.buildCards(result.Items, { itemsContainer: elem, - shape: "auto", + shape: getPortraitShape(), showTitle: true, scalable: true, - showItemCounts: true, centerText: true, - overlayMoreButton: true + overlayMoreButton: true, + allowBottomPadding: false }); } + }); + } + + function reloadItems(context, promise) { + + var query = getQuery(); + + promise.then(function (result) { + + var elem = context.querySelector('#items'); + var html = ''; + + var items = result.Items; + + for (var i = 0, length = items.length; i < length; i++) { + + var item = items[i]; + + html += '
'; + html += '

'; + html += item.Name; + html += '

'; + + if (enableScrollX()) { + html += '
'; + } else { + html += '
'; + } + html += '
'; + + html += '
'; + } + + elem.innerHTML = html; + + lazyLoader.lazyChildren(elem, fillItemsContainer); + libraryBrowser.saveQueryValues(getSavedQueryKey(), query); Dashboard.hideLoadingMsg(); diff --git a/dashboard-ui/scripts/tvrecommended.js b/dashboard-ui/scripts/tvrecommended.js index 73e6afecce..7e9b3a41ee 100644 --- a/dashboard-ui/scripts/tvrecommended.js +++ b/dashboard-ui/scripts/tvrecommended.js @@ -159,14 +159,14 @@ depends.push('scripts/tvshows'); break; case 4: - depends.push('scripts/episodes'); - break; - case 5: depends.push('scripts/tvgenres'); break; - case 6: + case 5: depends.push('scripts/tvstudios'); break; + case 6: + depends.push('scripts/episodes'); + break; default: break; } diff --git a/dashboard-ui/scripts/tvshows.js b/dashboard-ui/scripts/tvshows.js index 6da561a272..9bb2f1c7ae 100644 --- a/dashboard-ui/scripts/tvshows.js +++ b/dashboard-ui/scripts/tvshows.js @@ -1,4 +1,4 @@ -define(['events', 'libraryBrowser', 'imageLoader', 'alphaPicker', 'listView', 'cardBuilder', 'apphost', 'emby-itemscontainer'], function (events, libraryBrowser, imageLoader, alphaPicker, listView, cardBuilder, appHost) { +define(['events', 'libraryBrowser', 'imageLoader', 'listView', 'cardBuilder', 'apphost', 'emby-itemscontainer'], function (events, libraryBrowser, imageLoader, listView, cardBuilder, appHost) { 'use strict'; return function (view, params, tabContent) { @@ -220,26 +220,10 @@ function updateFilterControls(tabContent) { - var query = getQuery(tabContent); - self.alphaPicker.value(query.NameStartsWithOrGreater); } function initPage(tabContent) { - var alphaPickerElement = tabContent.querySelector('.alphaPicker'); - alphaPickerElement.addEventListener('alphavaluechanged', function (e) { - var newValue = e.detail.value; - var query = getQuery(tabContent); - query.NameStartsWithOrGreater = newValue; - query.StartIndex = 0; - reloadItems(tabContent); - }); - - self.alphaPicker = new alphaPicker({ - element: alphaPickerElement, - valueChangeEvent: 'click' - }); - tabContent.querySelector('.btnFilter').addEventListener('click', function () { self.showFilterMenu(); }); diff --git a/dashboard-ui/tv.html b/dashboard-ui/tv.html index 12009f235f..53e08918ad 100644 --- a/dashboard-ui/tv.html +++ b/dashboard-ui/tv.html @@ -15,14 +15,14 @@
${TabShows}
- - +
@@ -81,16 +81,26 @@
-
-
- -
+
-
+
+
+ +
+
+
+
+
+
+
+
+
+
+
@@ -103,19 +113,6 @@
-
-
- -
-
-
-
-
-
-
-
-
-