From 9a4a0e78a9ec5e3027fc74d7467100ba437f9d09 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 6 Jul 2016 16:16:56 -0400 Subject: [PATCH 1/6] voice fixes --- .../voice/commands/controlcommands.js | 10 +- .../voice/commands/disablecommands.js | 11 +- .../voice/commands/enablecommands.js | 11 +- .../voice/commands/playcommands.js | 84 ++++---- .../voice/commands/searchcommands.js | 1 - .../voice/commands/showcommands.js | 191 +++++++++++------- .../voice/commands/togglecommands.js | 12 +- .../emby-webcomponents/voice/voicecommands.js | 70 +++---- .../emby-webcomponents/voice/voicedialog.js | 7 +- .../voice/voiceprocessor.js | 5 +- dashboard-ui/scripts/wizardcomponents.js | 4 +- 11 files changed, 231 insertions(+), 175 deletions(-) diff --git a/dashboard-ui/bower_components/emby-webcomponents/voice/commands/controlcommands.js b/dashboard-ui/bower_components/emby-webcomponents/voice/commands/controlcommands.js index c1b46cc0e7..70e1ffde6a 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/voice/commands/controlcommands.js +++ b/dashboard-ui/bower_components/emby-webcomponents/voice/commands/controlcommands.js @@ -1,9 +1,15 @@ define(['playbackManager'], function (playbackManager) { + function setActiveDevice(name) { + return function () { + playbackManager.trySetActiveDeviceName(name); + }; + } + return function (result) { - result.success = true; + if (result.properties.devicename) { - playbackManager.trySetActiveDeviceName(result.properties.devicename); + return setActiveDevice(result.properties.devicename); } return; } diff --git a/dashboard-ui/bower_components/emby-webcomponents/voice/commands/disablecommands.js b/dashboard-ui/bower_components/emby-webcomponents/voice/commands/disablecommands.js index 20f1e004f6..908ebc109a 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/voice/commands/disablecommands.js +++ b/dashboard-ui/bower_components/emby-webcomponents/voice/commands/disablecommands.js @@ -1,13 +1,18 @@ define(['inputManager'], function (inputManager) { + function disableDisplayMirror() { + return function () { + inputManager.trigger('disabledisplaymirror'); + }; + } + return function (result) { - result.success = true; + switch (result.item.deviceid) { case 'displaymirroring': - inputManager.trigger('disabledisplaymirror'); + return disableDisplayMirror(); break; default: - result.success = false; return; } } diff --git a/dashboard-ui/bower_components/emby-webcomponents/voice/commands/enablecommands.js b/dashboard-ui/bower_components/emby-webcomponents/voice/commands/enablecommands.js index af88c8e432..2abbb86ea7 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/voice/commands/enablecommands.js +++ b/dashboard-ui/bower_components/emby-webcomponents/voice/commands/enablecommands.js @@ -1,13 +1,18 @@ define(['inputManager'], function (inputManager) { + function enableDisplayMirror() { + return function () { + inputManager.trigger('enabledisplaymirror'); + }; + } + return function (result) { - result.success = true; + switch (result.item.deviceid) { case 'displaymirroring': - inputManager.trigger('enabledisplaymirror'); + return enableDisplayMirror(); break; default: - result.success = false; return; } } diff --git a/dashboard-ui/bower_components/emby-webcomponents/voice/commands/playcommands.js b/dashboard-ui/bower_components/emby-webcomponents/voice/commands/playcommands.js index 7aedf49c6f..dcc659584c 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/voice/commands/playcommands.js +++ b/dashboard-ui/bower_components/emby-webcomponents/voice/commands/playcommands.js @@ -49,58 +49,52 @@ } return function (result) { - result.success = false; - var query = { + return function () { + var query = { - Limit: result.item.limit || 100, - UserId: result.userId, - ExcludeLocationTypes: "Virtual" - }; + Limit: result.item.limit || 100, + UserId: result.userId, + ExcludeLocationTypes: "Virtual" + }; - if (result.item.itemType) { - query.IncludeItemTypes = result.item.itemType; - } + if (result.item.itemType) { + query.IncludeItemTypes = result.item.itemType; + } - var apiClient = connectionManager.currentApiClient(); - if (result.item.sourceid === 'nextup') { + var apiClient = connectionManager.currentApiClient(); + if (result.item.sourceid === 'nextup') { - apiClient.getNextUpEpisodes(query).then(function (queryResult) { + apiClient.getNextUpEpisodes(query).then(function (queryResult) { + + playItems(queryResult.Items, result.item.shuffle); + + }); + } + + if (result.item.shuffle) { + result.item.sortBy = result.sortBy ? 'Random,' + result.item.sortBy : 'Random'; + } + + query.SortBy = result.item.sortBy; + query.SortOrder = result.item.sortOrder; + query.Recursive = true; + + if (result.item.filters.indexOf('unplayed') !== -1) { + query.IsPlayed = false; + } + if (result.item.filters.indexOf('played') !== -1) { + query.IsPlayed = true; + } + if (result.item.filters.indexOf('favorite') !== -1) { + query.Filters = 'IsFavorite'; + } + + + apiClient.getItems(apiClient.getCurrentUserId(), query).then(function (queryResult) { playItems(queryResult.Items, result.item.shuffle); - }); - result.success = true; - return; - } - - if (result.item.shuffle) { - result.item.sortBy = result.sortBy ? 'Random,' + result.item.sortBy : 'Random'; - } - - query.SortBy = result.item.sortBy; - query.SortOrder = result.item.sortOrder; - query.Recursive = true; - - if (result.item.filters.indexOf('unplayed') !== -1) { - query.IsPlayed = false; - } - if (result.item.filters.indexOf('played') !== -1) { - query.IsPlayed = true; - } - if (result.item.filters.indexOf('favorite') !== -1) { - query.Filters = 'IsFavorite'; - } - - - apiClient.getItems(apiClient.getCurrentUserId(), query).then(function (queryResult) { - - playItems(queryResult.Items, result.item.shuffle); - }); - - result.success = true; - - return; - + }; } }); \ No newline at end of file diff --git a/dashboard-ui/bower_components/emby-webcomponents/voice/commands/searchcommands.js b/dashboard-ui/bower_components/emby-webcomponents/voice/commands/searchcommands.js index 715114f9ca..4abf767677 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/voice/commands/searchcommands.js +++ b/dashboard-ui/bower_components/emby-webcomponents/voice/commands/searchcommands.js @@ -3,7 +3,6 @@ return function (result) { switch (result.item.deviceid) { default: - result.success = false; return; } } diff --git a/dashboard-ui/bower_components/emby-webcomponents/voice/commands/showcommands.js b/dashboard-ui/bower_components/emby-webcomponents/voice/commands/showcommands.js index 4446af9b53..08676adce4 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/voice/commands/showcommands.js +++ b/dashboard-ui/bower_components/emby-webcomponents/voice/commands/showcommands.js @@ -1,96 +1,131 @@ define(['inputManager', 'connectionManager', 'embyRouter'], function (inputManager, connectionManager, embyRouter) { - return function (result) { - result.success = true; - switch (result.item.sourceid) { - case 'music': - inputManager.trigger('music'); - break; - case 'movies': - if (result.properties.movieName) { + function getMusicCommand(result) { + return function () { + inputManager.trigger('music'); + }; + } - //TODO: Find a way to display movie - var query = { - Limit: 1, - UserId: result.userId, - ExcludeLocationTypes: "Virtual", - NameStartsWith: result.item.itemType - }; + function getMoviesCommand(result) { + return function () { + if (result.properties.movieName) { - if (result.item.itemType) { - query.IncludeItemTypes = result.item.itemType; - } + //TODO: Find a way to display movie + var query = { + Limit: 1, + UserId: result.userId, + ExcludeLocationTypes: "Virtual", + NameStartsWith: result.item.itemType + }; - var apiClient = connectionManager.currentApiClient(); - apiClient.getItems(apiClient.getCurrentUserId(), query).then(function (queryResult) { - - if (queryResult.Items.length) { - embyRouter.showItem(queryResult.Items[0]); - } - }); - - } else { - inputManager.trigger('movies'); + if (result.item.itemType) { + query.IncludeItemTypes = result.item.itemType; } - break; - case 'tvseries': - inputManager.trigger('tv'); - break; - case 'livetv': - var act = result.item.menuid; - if (act) { - if (act.indexOf('livetv') != -1) { - inputManager.trigger('livetv'); - } else if (act.indexOf('guide') != -1) { - inputManager.trigger('guide'); - } else if (act.indexOf('channels') != -1) { - inputManager.trigger('livetv'); - } else if (act.indexOf('recordings') != -1) { - inputManager.trigger('recordedtv'); - } else if (act.indexOf('scheduled') != -1) { - inputManager.trigger('recordedtv'); - } else if (act.indexOf('series') != -1) { - inputManager.trigger('recordedtv'); - } else { - inputManager.trigger('livetv'); + var apiClient = connectionManager.currentApiClient(); + apiClient.getItems(apiClient.getCurrentUserId(), query).then(function (queryResult) { + + if (queryResult.Items.length) { + embyRouter.showItem(queryResult.Items[0]); } + }); + + } else { + inputManager.trigger('movies'); + } + }; + } + + function getTVCommand(result) { + return function () { + inputManager.trigger('tv'); + }; + } + + function getLiveTVCommand(result) { + return function () { + var act = result.item.menuid; + if (act) { + if (act.indexOf('livetv') != -1) { + inputManager.trigger('livetv'); + } else if (act.indexOf('guide') != -1) { + inputManager.trigger('guide'); + } else if (act.indexOf('channels') != -1) { + inputManager.trigger('livetv'); + } else if (act.indexOf('recordings') != -1) { + inputManager.trigger('recordedtv'); + } else if (act.indexOf('scheduled') != -1) { + inputManager.trigger('recordedtv'); + } else if (act.indexOf('series') != -1) { + inputManager.trigger('recordedtv'); } else { inputManager.trigger('livetv'); } - break; - case 'recordings': - inputManager.trigger('recordedtv'); - break; - case 'latestepisodes': - inputManager.trigger('latestepisodes'); - case 'home': - var act = result.item.menuid; - if (act) { - if (act.indexOf('home') != -1) { - inputManager.trigger('home'); - } - else if (act.indexOf('nextup') != -1) { - inputManager.trigger('nextup'); - } - else if (act.indexOf('favorites') != -1) { - inputManager.trigger('favorites'); - } else if (act.indexOf('upcoming') != -1) { - inputManager.trigger('upcomingtv'); - } - else if (act.indexOf('nowplaying') != -1) { - inputManager.trigger('nowplaying'); - } - else { - inputManager.trigger('home'); - } - } else { + } else { + inputManager.trigger('livetv'); + } + }; + } + + function getRecordingsCommand(result) { + return function () { + inputManager.trigger('recordedtv'); + }; + } + + function getLatestEpisodesCommand(result) { + return function () { + inputManager.trigger('latestepisodes'); + }; + } + + function getHomeCommand(result) { + return function () { + var act = result.item.menuid; + if (act) { + if (act.indexOf('home') != -1) { inputManager.trigger('home'); } + else if (act.indexOf('nextup') != -1) { + inputManager.trigger('nextup'); + } + else if (act.indexOf('favorites') != -1) { + inputManager.trigger('favorites'); + } else if (act.indexOf('upcoming') != -1) { + inputManager.trigger('upcomingtv'); + } + else if (act.indexOf('nowplaying') != -1) { + inputManager.trigger('nowplaying'); + } + else { + inputManager.trigger('home'); + } + } else { + inputManager.trigger('home'); + } + }; + } + + return function (result) { + + switch (result.item.sourceid) { + case 'music': + return getMusicCommand(result); + case 'movies': + return getMoviesCommand(result); + case 'tvseries': + return getTVCommand(result); + case 'livetv': + return getLiveTVCommand(result); + case 'recordings': + return getRecordingsCommand(result); + case 'latestepisodes': + return getLatestEpisodesCommand(result); + case 'home': + return getHomeCommand(result); case 'group': - break; + return; default: - result.success = false; return; } diff --git a/dashboard-ui/bower_components/emby-webcomponents/voice/commands/togglecommands.js b/dashboard-ui/bower_components/emby-webcomponents/voice/commands/togglecommands.js index 9fa7382bd0..f9b2314e65 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/voice/commands/togglecommands.js +++ b/dashboard-ui/bower_components/emby-webcomponents/voice/commands/togglecommands.js @@ -1,14 +1,20 @@ define(['inputManager'], function (inputManager) { + function toggleDisplayMirror() { + return function () { + inputManager.trigger('toggledisplaymirror'); + }; + } + return function (result) { - result.success = true; + switch (result.item.deviceid) { case 'displaymirroring': - inputManager.trigger('toggledisplaymirror'); + return toggleDisplayMirror(); break; default: - result.success = false; return; } } + }); \ No newline at end of file diff --git a/dashboard-ui/bower_components/emby-webcomponents/voice/voicecommands.js b/dashboard-ui/bower_components/emby-webcomponents/voice/voicecommands.js index 0463cdf6e1..56169c9fd7 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/voice/voicecommands.js +++ b/dashboard-ui/bower_components/emby-webcomponents/voice/voicecommands.js @@ -11,11 +11,15 @@ define(['require'], function (require) { return new Promise(function (resolve, reject) { require([commandPath], function (command) { - command(result); - if (result.success) { + + var fn = command(result); + + if (fn) { + result.fn = fn; resolve(result); + } else { + reject(); } - reject(); }); }); @@ -23,38 +27,34 @@ define(['require'], function (require) { return function (result) { - return new Promise(function (resolve, reject) { + switch (result.item.actionid) { - switch (result.item.actionid) { - - case 'show': - processCommand('./commands/showcommands.js', result).then(function (result) { resolve(result); }); - break; - case 'play': - processCommand('./commands/playcommands.js', result).then(function (result) { resolve(result); }); - break; - case 'shuffle': - processCommand('./commands/playcommands.js', result).then(function (result) { resolve(result); }); - break; - case 'search': - processCommand('./commands/searchcommands.js', result).then(function (result) { resolve(result); }); - break; - case 'control': - processCommand('./commands/controlcommands.js', result).then(function (result) { resolve(result); }); - break; - case 'enable': - processCommand('./commands/enablecommands.js', result).then(function (result) { resolve(result); }); - break; - case 'disable': - processCommand('./commands/disablecommands.js', result).then(function (result) { resolve(result); }); - break; - case 'toggle': - processCommand('./commands/togglecommands.js', result).then(function (result) { resolve(result); }); - break; - default: - reject(); - return; - } - }); + case 'show': + return processCommand('./commands/showcommands.js', result); + break; + case 'play': + return processCommand('./commands/playcommands.js', result); + break; + case 'shuffle': + return processCommand('./commands/playcommands.js', result); + break; + case 'search': + return processCommand('./commands/searchcommands.js', result); + break; + case 'control': + return processCommand('./commands/controlcommands.js', result); + break; + case 'enable': + return processCommand('./commands/enablecommands.js', result); + break; + case 'disable': + return processCommand('./commands/disablecommands.js', result); + break; + case 'toggle': + return processCommand('./commands/togglecommands.js', result); + break; + default: + return Promise.reject(); + } } }); \ No newline at end of file diff --git a/dashboard-ui/bower_components/emby-webcomponents/voice/voicedialog.js b/dashboard-ui/bower_components/emby-webcomponents/voice/voicedialog.js index a7347339bc..8b6d038656 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/voice/voicedialog.js +++ b/dashboard-ui/bower_components/emby-webcomponents/voice/voicedialog.js @@ -247,10 +247,15 @@ define(['dialogHelper', './voicereceiver', './voiceprocessor', 'globalize', 'emb listen(); } function listen() { - voicereceiver.listenForCommand(lang || "en-US").then(processInput).then(function (data) { + voicereceiver.listenForCommand(lang || "en-US").then(processInput).then(function (result) { closeDialog(); + // Put a delay here in case navigation/popstate is involved. Allow that to flush out + setTimeout(function () { + result.fn(); + }, 1); + }, function (result) { if (result.error == 'group') { showVoiceHelp(result.item.groupid, result.groupName); diff --git a/dashboard-ui/bower_components/emby-webcomponents/voice/voiceprocessor.js b/dashboard-ui/bower_components/emby-webcomponents/voice/voiceprocessor.js index d193937b92..dfb8732ce2 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/voice/voiceprocessor.js +++ b/dashboard-ui/bower_components/emby-webcomponents/voice/voiceprocessor.js @@ -28,11 +28,12 @@ console.log("Command from Grammar Processor", processor); return voicecommands(processor) .then(function (result) { + console.log("Result of executed command", result); if (result.item.actionid === 'show' && result.item.sourceid === 'group') { - return Promise.resolve({ error: "group", item: result.item, groupName: result.name }); + return Promise.resolve({ error: "group", item: result.item, groupName: result.name, fn: result.fn }); } else { - return Promise.resolve({ item: result.item }); + return Promise.resolve({ item: result.item, fn: result.fn }); } }, function () { return Promise.reject({ error: "unrecognized-command", text: text }); diff --git a/dashboard-ui/scripts/wizardcomponents.js b/dashboard-ui/scripts/wizardcomponents.js index 2943a28dfa..875e92bc1c 100644 --- a/dashboard-ui/scripts/wizardcomponents.js +++ b/dashboard-ui/scripts/wizardcomponents.js @@ -18,7 +18,7 @@ view.querySelector('.fldSelectEncoderPathType').classList.remove('hide'); } - /*if (systemInfo.OperatingSystem == 'Windows' && systemInfo.SystemArchitecture != 'Arm') { + if (systemInfo.OperatingSystem == 'Windows' && systemInfo.SystemArchitecture != 'Arm') { view.querySelector('.suggestedLocation').innerHTML = Globalize.translate('FFmpegSuggestedDownload', 'https://ffmpeg.zeranoe.com'); @@ -29,7 +29,7 @@ instructions = 'Download FFmpeg 64-Bit Static'; } - } else*/ if (systemInfo.OperatingSystem == 'Linux' && systemInfo.SystemArchitecture != 'Arm') { + } else if (systemInfo.OperatingSystem == 'Linux' && systemInfo.SystemArchitecture != 'Arm') { view.querySelector('.suggestedLocation').innerHTML = Globalize.translate('FFmpegSuggestedDownload', 'http://johnvansickle.com/ffmpeg'); From 5d6cee12235fe2e9ca716b69c2eaa333e6b3ed7e Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 7 Jul 2016 00:28:58 -0400 Subject: [PATCH 2/6] update components --- .../emby-webcomponents/.bower.json | 8 +++---- .../emby-select/emby-select.css | 21 ++++++++++++++++--- .../emby-select/emby-select.js | 11 ++++++++++ .../emby-webcomponents/images/imagehelper.js | 4 ---- 4 files changed, 33 insertions(+), 11 deletions(-) diff --git a/dashboard-ui/bower_components/emby-webcomponents/.bower.json b/dashboard-ui/bower_components/emby-webcomponents/.bower.json index e757b139c8..c1ea36e456 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.69", - "_release": "1.4.69", + "version": "1.4.70", + "_release": "1.4.70", "_resolution": { "type": "version", - "tag": "1.4.69", - "commit": "24a5ea44282dc2e0a537c6245aedd88877ebb9ad" + "tag": "1.4.70", + "commit": "d1263e18e331a87300ac4580536b26931cea87c9" }, "_source": "https://github.com/MediaBrowser/emby-webcomponents.git", "_target": "^1.2.0", diff --git a/dashboard-ui/bower_components/emby-webcomponents/emby-select/emby-select.css b/dashboard-ui/bower_components/emby-webcomponents/emby-select/emby-select.css index 8045e5327f..532a73decf 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/emby-select/emby-select.css +++ b/dashboard-ui/bower_components/emby-webcomponents/emby-select/emby-select.css @@ -20,14 +20,29 @@ cursor: pointer; outline: none !important; width: 100%; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; } -[is="emby-select"] option { - color: initial; -} + [is="emby-select"] option { + color: initial; + } .selectContainer { margin-bottom: 2em; + position: relative; +} + +.selectArrowContainer { + position: absolute; + right: 0; + top: .25em; + color: inherit; +} + +.selectArrow { + margin-top: .35em; } .selectLabel { diff --git a/dashboard-ui/bower_components/emby-webcomponents/emby-select/emby-select.js b/dashboard-ui/bower_components/emby-webcomponents/emby-select/emby-select.js index 307dcb4635..9ec42c10ba 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/emby-select/emby-select.js +++ b/dashboard-ui/bower_components/emby-webcomponents/emby-select/emby-select.js @@ -153,6 +153,17 @@ var div = document.createElement('div'); div.classList.add('emby-select-selectionbar'); this.parentNode.insertBefore(div, this.nextSibling); + + var arrowContainer = document.createElement('div'); + arrowContainer.classList.add('selectArrowContainer'); + arrowContainer.innerHTML = '
0
'; + this.parentNode.appendChild(arrowContainer); + + var arrow = document.createElement('i'); + arrow.classList.add('md-icon'); + arrow.classList.add('selectArrow'); + arrow.innerHTML = ''; + arrowContainer.appendChild(arrow); } }; diff --git a/dashboard-ui/bower_components/emby-webcomponents/images/imagehelper.js b/dashboard-ui/bower_components/emby-webcomponents/images/imagehelper.js index df22363c3e..fac860e669 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/images/imagehelper.js +++ b/dashboard-ui/bower_components/emby-webcomponents/images/imagehelper.js @@ -125,10 +125,6 @@ define(['visibleinviewport', 'imageFetcher', 'layoutManager', 'events', 'browser fillImage(target); filledCount++; } - - if (filledCount >= images.length) { - observer.disconnect(); - } }, options ); From d8c62ad7bbfc338fe33a98a1e5bff4b9ea7c29a6 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 7 Jul 2016 00:29:08 -0400 Subject: [PATCH 3/6] fix chromecast setting save --- dashboard-ui/scripts/mypreferenceslanguages.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dashboard-ui/scripts/mypreferenceslanguages.js b/dashboard-ui/scripts/mypreferenceslanguages.js index 95ad01880d..a9c1448a5b 100644 --- a/dashboard-ui/scripts/mypreferenceslanguages.js +++ b/dashboard-ui/scripts/mypreferenceslanguages.js @@ -53,7 +53,7 @@ page.querySelector('#selectMaxBitrate').value = appSettings.maxStreamingBitrate(); } - page.querySelector('#selectMaxChromecastBitrate').value = appSettings.maxChromecastBitrate(); + page.querySelector('#selectMaxChromecastBitrate').value = appSettings.maxChromecastBitrate() || ''; Dashboard.hideLoadingMsg(); }); From a1057972b494392308e7a91b56d442e06a3ddf68 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 7 Jul 2016 00:29:25 -0400 Subject: [PATCH 4/6] removed dead code --- dashboard-ui/scripts/librarybrowser.js | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/dashboard-ui/scripts/librarybrowser.js b/dashboard-ui/scripts/librarybrowser.js index 7d20237964..72665e2f99 100644 --- a/dashboard-ui/scripts/librarybrowser.js +++ b/dashboard-ui/scripts/librarybrowser.js @@ -101,17 +101,6 @@ var libraryBrowser = { getDefaultPageSize: function (key, defaultValue) { - return 100; - var saved = appStorage.getItem(key || pageSizeKey); - - if (saved) { - return parseInt(saved); - } - - if (defaultValue) { - return defaultValue; - } - return 100; }, From be8eed0411d7b789e5f9981c1a361f57f473cbab Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 7 Jul 2016 11:56:49 -0400 Subject: [PATCH 5/6] update nav drawer --- .../scroller/smoothscroller.js | 981 ++++++++++++++++++ .../components/navdrawer/navdrawer.css | 1 - dashboard-ui/livetv.html | 12 +- dashboard-ui/movies.html | 14 +- dashboard-ui/scripts/librarybrowser.js | 111 +- dashboard-ui/scripts/site.js | 5 + dashboard-ui/tv.html | 17 +- 7 files changed, 1076 insertions(+), 65 deletions(-) create mode 100644 dashboard-ui/bower_components/emby-webcomponents/scroller/smoothscroller.js diff --git a/dashboard-ui/bower_components/emby-webcomponents/scroller/smoothscroller.js b/dashboard-ui/bower_components/emby-webcomponents/scroller/smoothscroller.js new file mode 100644 index 0000000000..ac90cf6064 --- /dev/null +++ b/dashboard-ui/bower_components/emby-webcomponents/scroller/smoothscroller.js @@ -0,0 +1,981 @@ +define(['browser', 'layoutManager', 'scrollStyles'], function (browser, layoutManager) { + + /** +* Return type of the value. +* +* @param {Mixed} value +* +* @return {String} +*/ + function type(value) { + if (value == null) { + return String(value); + } + + if (typeof value === 'object' || typeof value === 'function') { + return Object.prototype.toString.call(value).match(/\s([a-z]+)/i)[1].toLowerCase() || 'object'; + } + + return typeof value; + } + + /** + * Event preventDefault & stopPropagation helper. + * + * @param {Event} event Event object. + * @param {Bool} noBubbles Cancel event bubbling. + * + * @return {Void} + */ + function stopDefault(event, noBubbles) { + event.preventDefault(); + if (noBubbles) { + event.stopPropagation(); + } + } + + /** + * Disables an event it was triggered on and unbinds itself. + * + * @param {Event} event + * + * @return {Void} + */ + function disableOneEvent(event) { + /*jshint validthis:true */ + stopDefault(event, 1); + this.removeEventListener(event.type, disableOneEvent); + } + + /** + * Check if variable is a number. + * + * @param {Mixed} value + *s + * @return {Boolean} + */ + function isNumber(value) { + return !isNaN(parseFloat(value)) && isFinite(value); + } + + /** + * Make sure that number is within the limits. + * + * @param {Number} number + * @param {Number} min + * @param {Number} max + * + * @return {Number} + */ + function within(number, min, max) { + return number < min ? min : number > max ? max : number; + } + + var pluginName = 'sly'; + var className = 'Sly'; + var namespace = pluginName; + + // Other global values + var dragInitEventNames = ['touchstart', 'mousedown']; + var dragInitEvents = 'touchstart.' + namespace + ' mousedown.' + namespace; + var dragMouseEvents = ['mousemove', 'mouseup']; + var dragTouchEvents = ['touchmove', 'touchend']; + var wheelEvent = (document.implementation.hasFeature('Event.wheel', '3.0') ? 'wheel' : 'mousewheel'); + var clickEvent = 'click.' + namespace; + var mouseDownEvent = 'mousedown.' + namespace; + var interactiveElements = ['INPUT', 'SELECT', 'TEXTAREA']; + var tmpArray = []; + var time; + + // Math shorthands + var abs = Math.abs; + var sqrt = Math.sqrt; + var pow = Math.pow; + var round = Math.round; + var max = Math.max; + var min = Math.min; + + var scrollerFactory = function (frame, options) { + + // Extend options + var o = extend({}, { + slidee: null, // Selector, DOM element, or jQuery object with DOM element representing SLIDEE. + horizontal: false, // Switch to horizontal mode. + + // Scrolling + scrollSource: null, // Element for catching the mouse wheel scrolling. Default is FRAME. + scrollBy: 0, // Pixels or items to move per one mouse scroll. 0 to disable scrolling. + scrollHijack: 300, // Milliseconds since last wheel event after which it is acceptable to hijack global scroll. + scrollTrap: false, // Don't bubble scrolling when hitting scrolling limits. + + // Dragging + dragSource: null, // Selector or DOM element for catching dragging events. Default is FRAME. + mouseDragging: 1, // Enable navigation by dragging the SLIDEE with mouse cursor. + touchDragging: 1, // Enable navigation by dragging the SLIDEE with touch events. + releaseSwing: false, // Ease out on dragging swing release. + swingSpeed: 0.2, // Swing synchronization speed, where: 1 = instant, 0 = infinite. + elasticBounds: false, // Stretch SLIDEE position limits when dragging past FRAME boundaries. + dragThreshold: 3, // Distance in pixels before Sly recognizes dragging. + intervactive: null, // Selector for special interactive elements. + + // Mixed options + speed: 0, // Animations speed in milliseconds. 0 to disable animations. + + // Classes + draggedClass: 'dragged', // Class for dragged elements (like SLIDEE or scrollbar handle). + activeClass: 'active', // Class for active items and pages. + disabledClass: 'disabled' // Class for disabled navigation elements. + }, options); + + var isSmoothScrollSupported = 'scrollBehavior' in document.documentElement.style; + + // native scroll is a must with touch input + // also use native scroll when scrolling vertically in desktop mode - excluding horizontal because the mouse wheel support is choppy at the moment + // in cases with firefox, if the smooth scroll api is supported then use that because their implementation is very good + if (browser.operaTv) { + // no scrolling supported + options.enableNativeScroll = false; + } + else if (browser.edge && !browser.xboxOne) { + // no scrolling supported + options.enableNativeScroll = false; + } + else if (isSmoothScrollSupported && browser.firefox) { + // native smooth scroll + options.enableNativeScroll = true; + } + else if (options.requireAnimation) { + + // transform is the only way to guarantee animation + options.enableNativeScroll = false; + } + else if (layoutManager.mobile || + layoutManager.desktop || + !browser.animate) { + + options.enableNativeScroll = true; + } + + // Private variables + var self = this; + self.options = o; + + // Frame + var frameElement = frame; + var slideeElement = o.slidee ? o.slidee : sibling(frameElement.firstChild)[0]; + var frameSize = 0; + var pos = { + start: 0, + center: 0, + end: 0, + cur: 0, + dest: 0 + }; + + var transform = !options.enableNativeScroll; + + var hPos = { + start: 0, + end: 0, + cur: 0 + }; + + // Items + var rel = { + activeItem: null + }; + + // Miscellaneous + var scrollSource = o.scrollSource ? o.scrollSource : frameElement; + var dragSourceElement = o.dragSource ? o.dragSource : frameElement; + var animation = {}; + var dragging = { + released: 1 + }; + var scrolling = { + last: 0, + delta: 0, + resetTime: 200 + }; + var historyID = 0; + var i, l; + + // Normalizing frame + frame = frameElement; + + // Expose properties + self.initialized = 0; + self.frame = frame; + self.slidee = slideeElement; + self.options = o; + self.dragging = dragging; + + function sibling(n, elem) { + var matched = []; + + for (; n; n = n.nextSibling) { + if (n.nodeType === 1 && n !== elem) { + matched.push(n); + } + } + return matched; + } + + /** + * Loading function. + * + * Populate arrays, set sizes, bind events, ... + * + * @param {Boolean} [isInit] Whether load is called from within self.init(). + * @return {Void} + */ + function load(isInit) { + + // Reset global variables + frameSize = getWidthOrHeight(frameElement, o.horizontal ? 'width' : 'height'); + var slideeSize = o.scrollWidth || Math.max(slideeElement[o.horizontal ? 'offsetWidth' : 'offsetHeight'], slideeElement[o.horizontal ? 'scrollWidth' : 'scrollHeight']); + + // Set position limits & relativess + pos.start = 0; + pos.end = max(slideeSize - frameSize, 0); + + if (!isInit) { + // Fix possible overflowing + slideTo(within(pos.dest, pos.start, pos.end)); + } + } + + var pnum = (/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/).source; + var rnumnonpx = new RegExp("^(" + pnum + ")(?!px)[a-z%]+$", "i"); + + function getWidthOrHeight(elem, name, extra) { + + // Start with offset property, which is equivalent to the border-box value + var valueIsBorderBox = true, + val = name === "width" ? elem.offsetWidth : elem.offsetHeight, + styles = getComputedStyle(elem, null), + isBorderBox = styles.getPropertyValue("box-sizing") === "border-box"; + + // Some non-html elements return undefined for offsetWidth, so check for null/undefined + // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285 + // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668 + if (val <= 0 || val == null) { + // Fall back to computed then uncomputed css if necessary + //val = curCSS(elem, name, styles); + if (val < 0 || val == null) { + val = elem.style[name]; + } + + // Computed unit is not pixels. Stop here and return. + if (rnumnonpx.test(val)) { + return val; + } + + // Check for style in case a browser which returns unreliable values + // for getComputedStyle silently falls back to the reliable elem.style + valueIsBorderBox = isBorderBox && + (support.boxSizingReliable() || val === elem.style[name]); + + // Normalize "", auto, and prepare for extra + val = parseFloat(val) || 0; + } + + // Use the active box-sizing model to add/subtract irrelevant styles + return (val + + augmentWidthOrHeight( + elem, + name, + extra || (isBorderBox ? "border" : "content"), + valueIsBorderBox, + styles + ) + ); + } + + var cssExpand = ["Top", "Right", "Bottom", "Left"]; + + function augmentWidthOrHeight(elem, name, extra, isBorderBox, styles) { + var i = extra === (isBorderBox ? "border" : "content") ? + // If we already have the right measurement, avoid augmentation + 4 : + // Otherwise initialize for horizontal or vertical properties + name === "width" ? 1 : 0, + + val = 0; + + for (; i < 4; i += 2) { + // Both box models exclude margin, so add it if we want it + if (extra === "margin") { + //val += jQuery.css(elem, extra + cssExpand[i], true, styles); + } + + if (isBorderBox) { + // border-box includes padding, so remove it if we want content + if (extra === "content") { + //val -= jQuery.css(elem, "padding" + cssExpand[i], true, styles); + } + + // At this point, extra isn't border nor margin, so remove border + if (extra !== "margin") { + //val -= jQuery.css(elem, "border" + cssExpand[i] + "Width", true, styles); + } + } else { + // At this point, extra isn't content, so add padding + //val += jQuery.css(elem, "padding" + cssExpand[i], true, styles); + + // At this point, extra isn't content nor padding, so add border + if (extra !== "padding") { + //val += jQuery.css(elem, "border" + cssExpand[i] + "Width", true, styles); + } + } + } + + return val; + } + + self.reload = function () { load(); }; + + function nativeScrollTo(container, pos, immediate) { + + if (!immediate && container.scrollTo) { + if (o.horizontal) { + container.scrollTo(pos, 0); + } else { + container.scrollTo(0, pos); + } + } else { + if (o.horizontal) { + container.scrollLeft = Math.round(pos); + } else { + container.scrollTop = Math.round(pos); + } + } + } + + /** + * Animate to a position. + * + * @param {Int} newPos New position. + * @param {Bool} immediate Reposition immediately without an animation. + * + * @return {Void} + */ + function slideTo(newPos, immediate) { + + // Handle overflowing position limits + if (dragging.init && dragging.slidee && o.elasticBounds) { + if (newPos > pos.end) { + newPos = pos.end + (newPos - pos.end) / 6; + } else if (newPos < pos.start) { + newPos = pos.start + (newPos - pos.start) / 6; + } + } else { + newPos = within(newPos, pos.start, pos.end); + } + + if (!transform) { + + nativeScrollTo(slideeElement, newPos, immediate); + return; + } + + // Update the animation object + animation.from = pos.cur; + animation.to = newPos; + animation.tweesing = dragging.tweese || dragging.init && !dragging.slidee; + animation.immediate = !animation.tweesing && (immediate || dragging.init && dragging.slidee || !o.speed); + + // Reset dragging tweesing request + dragging.tweese = 0; + + // Start animation rendering + if (newPos !== pos.dest) { + pos.dest = newPos; + renderAnimate(animation); + } + } + + var scrollEvent = new CustomEvent("scroll"); + + function renderAnimate() { + + if (!transform) { + return; + } + + var obj = getComputedStyle(slideeElement, null).getPropertyValue('transform').match(/([-+]?(?:\d*\.)?\d+)\D*, ([-+]?(?:\d*\.)?\d+)\D*\)/); + if (obj) { + // [1] = x, [2] = y + pos.cur = parseInt(o.horizontal ? obj[1] : obj[2]) * -1; + } + + var keyframes; + + animation.to = round(animation.to); + + if (o.horizontal) { + keyframes = [ + { transform: 'translate3d(' + (-round(pos.cur || animation.from)) + 'px, 0, 0)', offset: 0 }, + { transform: 'translate3d(' + (-round(animation.to)) + 'px, 0, 0)', offset: 1 } + ]; + } else { + keyframes = [ + { transform: 'translate3d(0, ' + (-round(pos.cur || animation.from)) + 'px, 0)', offset: 0 }, + { transform: 'translate3d(0, ' + (-round(animation.to)) + 'px, 0)', offset: 1 } + ]; + } + + var speed = o.speed; + + if (animation.immediate) { + speed = o.immediateSpeed || 50; + if (!browser.animate) { + o.immediateSpeed = 0; + } + } + + var animationConfig = { + duration: speed, + iterations: 1, + fill: 'both' + }; + + if (!animation.immediate || browser.animate) { + animationConfig.easing = 'ease-out'; + } + + var animationInstance = slideeElement.animate(keyframes, animationConfig); + + animationInstance.onfinish = function () { + pos.cur = animation.to; + document.dispatchEvent(scrollEvent); + }; + } + + function getOffsets(elems) { + + var doc = document; + var results = []; + + if (!doc) { + return results; + } + + var docElem = doc.documentElement; + var docElemValues = { + clientTop: docElem.clientTop, + clientLeft: docElem.clientLeft + }; + + var win = doc.defaultView; + var winValues = { + pageXOffset: win.pageXOffset, + pageYOffset: win.pageYOffset + }; + + var box; + var elem; + + for (var i = 0, length = elems.length; i < length; i++) { + + elem = elems[i]; + // Support: BlackBerry 5, iOS 3 (original iPhone) + // If we don't have gBCR, just use 0,0 rather than error + if (elem.getBoundingClientRect) { + box = elem.getBoundingClientRect(); + } else { + box = { top: 0, left: 0 }; + } + + results[i] = { + top: box.top + winValues.pageYOffset - docElemValues.clientTop, + left: box.left + winValues.pageXOffset - docElemValues.clientLeft + }; + } + + return results; + } + + /** + * Returns the position object. + * + * @param {Mixed} item + * + * @return {Object} + */ + self.getPos = function (item) { + + var offsets = getOffsets([slideeElement, item]); + + var slideeOffset = offsets[0]; + var itemOffset = offsets[1]; + + var offset = o.horizontal ? itemOffset.left - slideeOffset.left : itemOffset.top - slideeOffset.top; + var size = item[o.horizontal ? 'offsetWidth' : 'offsetHeight']; + + var centerOffset = o.centerOffset || 0; + + if (!transform) { + centerOffset = 0; + if (o.horizontal) { + offset += slideeElement.scrollLeft; + } else { + offset += slideeElement.scrollTop; + } + } + + return { + start: offset, + center: offset + centerOffset - (frameSize / 2) + (size / 2), + end: offset - frameSize + size, + size: size + }; + }; + + /** + * Slide SLIDEE by amount of pixels. + * + * @param {Int} delta Pixels/Items. Positive means forward, negative means backward. + * @param {Bool} immediate Reposition immediately without an animation. + * + * @return {Void} + */ + self.slideBy = function (delta, immediate) { + if (!delta) { + return; + } + slideTo(pos.dest + delta, immediate); + }; + + /** + * Animate SLIDEE to a specific position. + * + * @param {Int} pos New position. + * @param {Bool} immediate Reposition immediately without an animation. + * + * @return {Void} + */ + self.slideTo = function (pos, immediate) { + slideTo(pos, immediate); + }; + + /** + * Core method for handling `toLocation` methods. + * + * @param {String} location + * @param {Mixed} item + * @param {Bool} immediate + * + * @return {Void} + */ + function to(location, item, immediate) { + // Optional arguments logic + if (type(item) === 'boolean') { + immediate = item; + item = undefined; + v + } + + if (item === undefined) { + slideTo(pos[location], immediate); + } else { + + //if (!transform) { + + // item.scrollIntoView(); + // return; + //} + + var itemPos = self.getPos(item); + if (itemPos) { + slideTo(itemPos[location], immediate, true); + } + } + } + + /** + * Animate element or the whole SLIDEE to the start of the frame. + * + * @param {Mixed} item Item DOM element, or index starting at 0. Omitting will animate SLIDEE. + * @param {Bool} immediate Reposition immediately without an animation. + * + * @return {Void} + */ + self.toStart = function (item, immediate) { + to('start', item, immediate); + }; + + /** + * Animate element or the whole SLIDEE to the end of the frame. + * + * @param {Mixed} item Item DOM element, or index starting at 0. Omitting will animate SLIDEE. + * @param {Bool} immediate Reposition immediately without an animation. + * + * @return {Void} + */ + self.toEnd = function (item, immediate) { + to('end', item, immediate); + }; + + /** + * Animate element or the whole SLIDEE to the center of the frame. + * + * @param {Mixed} item Item DOM element, or index starting at 0. Omitting will animate SLIDEE. + * @param {Bool} immediate Reposition immediately without an animation. + * + * @return {Void} + */ + self.toCenter = function (item, immediate) { + to('center', item, immediate); + }; + + function extend() { + for (var i = 1; i < arguments.length; i++) + for (var key in arguments[i]) + if (arguments[i].hasOwnProperty(key)) + arguments[0][key] = arguments[i][key]; + return arguments[0]; + } + + /** + * Keeps track of a dragging delta history. + * + * @return {Void} + */ + function draggingHistoryTick() { + // Looking at this, I know what you're thinking :) But as we need only 4 history states, doing it this way + // as opposed to a proper loop is ~25 bytes smaller (when minified with GCC), a lot faster, and doesn't + // generate garbage. The loop version would create 2 new variables on every tick. Unexaptable! + dragging.history[0] = dragging.history[1]; + dragging.history[1] = dragging.history[2]; + dragging.history[2] = dragging.history[3]; + dragging.history[3] = dragging.delta; + } + + /** + * Initialize continuous movement. + * + * @return {Void} + */ + function continuousInit(source) { + dragging.released = 0; + dragging.source = source; + dragging.slidee = source === 'slidee'; + } + + function dragInitSlidee(event) { + dragInit(event, 'slidee'); + } + + /** + * Dragging initiator. + *a + * @param {Event} event + * + * @return {Void} + */ + function dragInit(event, source) { + var isTouch = event.type === 'touchstart'; + var isSlidee = source === 'slidee'; + + // Ignore when already in progress, or interactive element in non-touch navivagion + if (dragging.init || !isTouch && isInteractive(event.target)) { + return; + } + + // SLIDEE dragging conditions + if (isSlidee && !(isTouch ? o.touchDragging : o.mouseDragging && event.which < 2)) { + return; + } + + if (!isTouch) { + // prevents native image dragging in Firefox + stopDefault(event); + } + + // Reset dragging object + continuousInit(source); + + // Properties used in dragHandler + dragging.init = 0; + dragging.source = event.target; + dragging.touch = isTouch; + dragging.pointer = isTouch ? event.touches[0] : event; + dragging.initX = dragging.pointer.pageX; + dragging.initY = dragging.pointer.pageY; + dragging.initPos = isSlidee ? pos.cur : hPos.cur; + dragging.start = +new Date(); + dragging.time = 0; + dragging.path = 0; + dragging.delta = 0; + dragging.locked = 0; + dragging.history = [0, 0, 0, 0]; + dragging.pathToLock = isSlidee ? isTouch ? 30 : 10 : 0; + + // Bind dragging events + if (isTouch) { + dragTouchEvents.forEach(function (eventName) { + document.addEventListener(eventName, dragHandler); + }); + } else { + dragMouseEvents.forEach(function (eventName) { + document.addEventListener(eventName, dragHandler); + }); + } + + // Add dragging class + if (isSlidee) { + slideeElement.classList.add(o.draggedClass); + } + + // Keep track of a dragging path history. This is later used in the + // dragging release swing calculation when dragging SLIDEE. + if (isSlidee) { + historyID = setInterval(draggingHistoryTick, 10); + } + } + + /** + * Handler for dragging scrollbar handle or SLIDEE. + * + * @param {Event} event + * + * @return {Void} + */ + function dragHandler(event) { + dragging.released = event.type === 'mouseup' || event.type === 'touchend'; + dragging.pointer = dragging.touch ? event[dragging.released ? 'changedTouches' : 'touches'][0] : event; + dragging.pathX = dragging.pointer.pageX - dragging.initX; + dragging.pathY = dragging.pointer.pageY - dragging.initY; + dragging.path = sqrt(pow(dragging.pathX, 2) + pow(dragging.pathY, 2)); + dragging.delta = o.horizontal ? dragging.pathX : dragging.pathY; + + if (!dragging.released && dragging.path < 1) return; + + // We haven't decided whether this is a drag or not... + if (!dragging.init) { + // If the drag path was very short, maybe it's not a drag? + if (dragging.path < o.dragThreshold) { + // If the pointer was released, the path will not become longer and it's + // definitely not a drag. If not released yet, decide on next iteration + return dragging.released ? dragEnd() : undefined; + } else { + // If dragging path is sufficiently long we can confidently start a drag + // if drag is in different direction than scroll, ignore it + if (o.horizontal ? abs(dragging.pathX) > abs(dragging.pathY) : abs(dragging.pathX) < abs(dragging.pathY)) { + dragging.init = 1; + } else { + return dragEnd(); + } + } + } + + stopDefault(event); + + // Disable click on a source element, as it is unwelcome when dragging + if (!dragging.locked && dragging.path > dragging.pathToLock && dragging.slidee) { + dragging.locked = 1; + dragging.source.addEventListener('click', disableOneEvent); + } + + // Cancel dragging on release + if (dragging.released) { + dragEnd(); + + // Adjust path with a swing on mouse release + if (o.releaseSwing && dragging.slidee) { + dragging.swing = (dragging.delta - dragging.history[0]) / 40 * 300; + dragging.delta += dragging.swing; + dragging.tweese = abs(dragging.swing) > 10; + } + } + + slideTo(dragging.slidee ? round(dragging.initPos - dragging.delta) : handleToSlidee(dragging.initPos + dragging.delta)); + } + + /** + * Stops dragging and cleans up after it. + * + * @return {Void} + */ + function dragEnd() { + clearInterval(historyID); + dragging.released = true; + + if (dragging.touch) { + dragTouchEvents.forEach(function (eventName) { + document.removeEventListener(eventName, dragHandler); + }); + } else { + dragMouseEvents.forEach(function (eventName) { + document.removeEventListener(eventName, dragHandler); + }); + } + + if (dragging.slidee) { + slideeElement.classList.remove(o.draggedClass); + } + + // Make sure that disableOneEvent is not active in next tick. + setTimeout(function () { + dragging.source.removeEventListener('click', disableOneEvent); + }); + + dragging.init = 0; + } + + /** + * Check whether element is interactive. + * + * @return {Boolean} + */ + function isInteractive(element) { + + while (element) { + + if (interactiveElements.indexOf(element.tagName) != -1) { + return true; + } + + element = element.parentNode; + } + return false; + } + + /** + * Mouse wheel delta normalization. + * + * @param {Event} event + * + * @return {Int} + */ + function normalizeWheelDelta(event) { + // wheelDelta needed only for IE8- + scrolling.curDelta = ((o.horizontal ? event.deltaY || event.deltaX : event.deltaY) || -event.wheelDelta); + + if (transform) { + scrolling.curDelta /= event.deltaMode === 1 ? 3 : 100; + } + return scrolling.curDelta; + } + + /** + * Mouse scrolling handler. + * + * @param {Event} event + * + * @return {Void} + */ + function scrollHandler(event) { + + event[namespace] = self; + + // Ignore if there is no scrolling to be done + if (!o.scrollBy || pos.start === pos.end) { + return; + } + var delta = normalizeWheelDelta(event); + // Trap scrolling only when necessary and/or requested + if (o.scrollTrap || delta > 0 && pos.dest < pos.end || delta < 0 && pos.dest > pos.start) { + stopDefault(event, 1); + } + + if (transform) { + self.slideBy(o.scrollBy * delta); + } else { + + if (o.horizontal) { + slideeElement.scrollLeft += o.scrollBy * delta; + } else { + slideeElement.scrollTop += o.scrollBy * delta; + } + } + } + + /** + * Destroys instance and everything it created. + * + * @return {Void} + */ + self.destroy = function () { + + window.removeEventListener('resize', onResize, true); + scrollSource.removeEventListener(wheelEvent, scrollHandler); + + // Reset initialized status and return the instance + self.initialized = 0; + return self; + }; + + function onResize() { + load(false); + } + + /** + * Initialize. + * + * @return {Object} + */ + self.init = function () { + if (self.initialized) { + return; + } + + // Disallow multiple instances on the same element + if (frame.sly) throw new Error('There is already a Sly instance on this element'); + + frame.sly = true; + + // Set required styles + var movables = []; + if (slideeElement) { + movables.push(slideeElement); + } + + if (!transform) { + if (o.horizontal) { + if (layoutManager.desktop && o.hiddenScroll === false) { + slideeElement.classList.add('smoothScrollX'); + } else { + slideeElement.classList.add('hiddenScrollX'); + } + } else { + if (layoutManager.desktop && o.hiddenScroll === false) { + slideeElement.classList.add('smoothScrollY'); + } else { + slideeElement.classList.add('hiddenScrollY'); + } + } + } else { + slideeElement.style['will-change'] = 'transform'; + } + + // Scrolling navigation + scrollSource.addEventListener(wheelEvent, scrollHandler); + + if (transform) { + dragInitEventNames.forEach(function(eventName) { + dragSourceElement.addEventListener(eventName, dragInitSlidee); + }); + + window.addEventListener('resize', onResize, true); + } + + // Mark instance as initialized + self.initialized = 1; + + // Load + load(true); + + // Return instance + return self; + }; + }; + + scrollerFactory.create = function (frame, options) { + var instance = new scrollerFactory(frame, options); + return Promise.resolve(instance); + }; + + return scrollerFactory; +}); \ No newline at end of file diff --git a/dashboard-ui/components/navdrawer/navdrawer.css b/dashboard-ui/components/navdrawer/navdrawer.css index 8fd4db5a3f..dedb19dc8f 100644 --- a/dashboard-ui/components/navdrawer/navdrawer.css +++ b/dashboard-ui/components/navdrawer/navdrawer.css @@ -17,7 +17,6 @@ .touch-menu-la.transition { /*transition: transform 0.3s ease-out;*/ - transition: -webkit-transform ease-out 260ms, left ease-out 260ms; transition: transform ease-out 260ms, left ease-out 260ms; /*transition: -webkit-transform ease-in-out 0.3s, width ease-in-out 0.3s, visibility 0.3s; transition: transform ease-in-out 0.3s, width ease-in-out 0.3s, visibility 0.3s;*/ diff --git a/dashboard-ui/livetv.html b/dashboard-ui/livetv.html index a28d21bcd8..579c046347 100644 --- a/dashboard-ui/livetv.html +++ b/dashboard-ui/livetv.html @@ -1,11 +1,13 @@ 
- - - - - +
+ + + + + +
diff --git a/dashboard-ui/movies.html b/dashboard-ui/movies.html index 85749c7033..9dd5104dd7 100644 --- a/dashboard-ui/movies.html +++ b/dashboard-ui/movies.html @@ -1,12 +1,14 @@ 
- - - - - - +
+ + + + + + +
diff --git a/dashboard-ui/scripts/librarybrowser.js b/dashboard-ui/scripts/librarybrowser.js index 72665e2f99..a4906260da 100644 --- a/dashboard-ui/scripts/librarybrowser.js +++ b/dashboard-ui/scripts/librarybrowser.js @@ -1,4 +1,4 @@ -define(['scrollHelper', 'viewManager', 'appSettings', 'appStorage', 'apphost', 'datetime', 'itemHelper', 'mediaInfo', 'scrollStyles'], function (scrollHelper, viewManager, appSettings, appStorage, appHost, datetime, itemHelper, mediaInfo) { +define(['scrollHelper', 'viewManager', 'appSettings', 'appStorage', 'apphost', 'datetime', 'itemHelper', 'mediaInfo', 'scroller', 'scrollStyles'], function (scrollHelper, viewManager, appSettings, appStorage, appHost, datetime, itemHelper, mediaInfo, scroller) { function parentWithClass(elem, className) { @@ -264,8 +264,6 @@ } } - tabs.classList.add('hiddenScrollX'); - tabs.addEventListener('click', function (e) { var current = tabs.querySelector('.is-active'); @@ -296,6 +294,8 @@ fadeInRight(newPanel); } + tabs.selectedTabIndex = index; + tabs.dispatchEvent(new CustomEvent("tabchange", { detail: { selectedTabIndex: index @@ -303,13 +303,40 @@ })); newPanel.classList.add('is-active'); - - //scrollHelper.toCenter(tabs, link, true); }, 120); + + if (tabs.scroller) { + tabs.scroller.toCenter(link, false); + } } }); ownerpage.addEventListener('viewbeforeshow', LibraryBrowser.onTabbedpagebeforeshow); + + var contentScrollSlider = tabs.querySelector('.contentScrollSlider'); + if (contentScrollSlider) { + tabs.scroller = new scroller(tabs, { + horizontal: 1, + itemNav: 0, + mouseDragging: 1, + touchDragging: 1, + slidee: tabs.querySelector('.contentScrollSlider'), + smart: true, + releaseSwing: true, + scrollBy: 200, + speed: 120, + elasticBounds: 1, + dragHandle: 1, + dynamicHandle: 1, + clickBar: 1, + //centerOffset: window.innerWidth * .05, + hiddenScroll: true, + requireAnimation: true + }); + tabs.scroller.init(); + } else { + tabs.classList.add('hiddenScrollX'); + } }, onTabbedpagebeforeshow: function (e) { @@ -1633,7 +1660,6 @@ getPostersPerRow: function (screenWidth) { - var cache = true; function getValue(shape) { switch (shape) { @@ -1668,24 +1694,37 @@ if (screenWidth >= 770) return 3; if (screenWidth >= 420) return 2; return 1; + case 'smallBackdrop': + if (screenWidth >= 1440) return 6; + if (screenWidth >= 1100) return 6; + if (screenWidth >= 800) return 5; + if (screenWidth >= 600) return 4; + if (screenWidth >= 540) return 3; + if (screenWidth >= 420) return 2; + return 1; + case 'homePageSmallBackdrop': + if (screenWidth >= 1440) return 6; + if (screenWidth >= 1100) return 6; + if (screenWidth >= 800) return 5; + if (screenWidth >= 600) return 4; + if (screenWidth >= 540) return 3; + if (screenWidth >= 420) return 2; + return 1; + case 'overflowPortrait': + if (screenWidth >= 1000) return 100 / 23; + if (screenWidth >= 640) return 100 / 36; + return 2.5; + case 'overflowSquare': + if (screenWidth >= 1000) return 100 / 22; + if (screenWidth >= 640) return 100 / 30; + return 100 / 42; + case 'overflowBackdrop': + if (screenWidth >= 1000) return 100 / 40; + if (screenWidth >= 640) return 100 / 60; + return 100 / 84; default: - break; + return 4; } - var div = document.createElement('div'); - div.classList.add('card'); - div.classList.add(shape + 'Card'); - div.innerHTML = '
'; - document.body.appendChild(div); - var innerWidth = div.querySelector('.cardImage').clientWidth; - - if (!innerWidth || isNaN(innerWidth)) { - cache = false; - innerWidth = Math.min(400, screenWidth / 2); - } - - var width = screenWidth / innerWidth; - div.parentNode.removeChild(div); - return Math.floor(width); } var info = {}; @@ -1694,7 +1733,6 @@ var currentShape = LibraryBrowser.shapes[i]; info[currentShape] = getValue(currentShape); } - info.cache = cache; return info; }, @@ -1716,9 +1754,7 @@ var result = LibraryBrowser.getPosterViewInfoInternal(screenWidth); result.screenWidth = screenWidth; - if (result.cache) { - cachedResults.push(result); - } + cachedResults.push(result); return result; }, @@ -1730,31 +1766,14 @@ var result = {}; result.screenWidth = screenWidth; - if (AppInfo.hasLowImageBandwidth) { - if (!AppInfo.isNativeApp) { - screenWidth *= .75; - } - } else { - screenWidth *= 1.2; - } - - var roundTo = 100; - for (var i = 0, length = LibraryBrowser.shapes.length; i < length; i++) { var currentShape = LibraryBrowser.shapes[i]; var shapeWidth = screenWidth / imagesPerRow[currentShape]; - if (!browserInfo.mobile) { - - shapeWidth = Math.round(shapeWidth / roundTo) * roundTo; - } - result[currentShape + 'Width'] = Math.round(shapeWidth); } - result.cache = imagesPerRow.cache; - return result; }, @@ -1930,7 +1949,7 @@ width = posterWidth; height = primaryImageAspectRatio ? Math.round(posterWidth / primaryImageAspectRatio) : null; - imgUrl = ApiClient.getImageUrl(imageItem.Id, { + imgUrl = ApiClient.getScaledImageUrl(imageItem.Id, { type: "Primary", maxHeight: height, maxWidth: width, @@ -2015,7 +2034,7 @@ width = posterWidth; height = primaryImageAspectRatio ? Math.round(posterWidth / primaryImageAspectRatio) : null; - imgUrl = ApiClient.getImageUrl(imageItem.Id, { + imgUrl = ApiClient.getScaledImageUrl(imageItem.Id, { type: "Primary", maxHeight: height, maxWidth: width, @@ -2033,7 +2052,7 @@ } else if (imageItem.ParentPrimaryImageTag) { - imgUrl = ApiClient.getImageUrl(imageItem.ParentPrimaryImageItemId, { + imgUrl = ApiClient.getScaledImageUrl(imageItem.ParentPrimaryImageItemId, { type: "Primary", maxWidth: posterWidth, tag: item.ParentPrimaryImageTag, diff --git a/dashboard-ui/scripts/site.js b/dashboard-ui/scripts/site.js index cd5d0a5a70..ecfc0f1959 100644 --- a/dashboard-ui/scripts/site.js +++ b/dashboard-ui/scripts/site.js @@ -1179,6 +1179,10 @@ var Dashboard = { quality -= 10; } + if (browserInfo.mobile) { + quality -= 10; + } + if (AppInfo.hasLowImageBandwidth) { // The native app can handle a little bit more than safari @@ -1963,6 +1967,7 @@ var AppInfo = {}; define("swiper", [bowerPath + "/Swiper/dist/js/swiper.min", "css!" + bowerPath + "/Swiper/dist/css/swiper.min"], returnFirstDependency); + define("scroller", [embyWebComponentsBowerPath + "/scroller/smoothscroller"], returnFirstDependency); define("toast", [embyWebComponentsBowerPath + "/toast/toast"], returnFirstDependency); define("scrollHelper", [embyWebComponentsBowerPath + "/scrollhelper"], returnFirstDependency); diff --git a/dashboard-ui/tv.html b/dashboard-ui/tv.html index 3390550d63..c7da714c60 100644 --- a/dashboard-ui/tv.html +++ b/dashboard-ui/tv.html @@ -1,14 +1,17 @@ 
- - - - - - - +
+ + + + + + + +
+
From b4e32e14d4349cb9fb6d7b63f7c5d6ce0dfe5398 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 7 Jul 2016 11:58:32 -0400 Subject: [PATCH 6/6] update components --- .../bower_components/emby-webcomponents/.bower.json | 8 ++++---- dashboard-ui/bower_components/iron-selector/.bower.json | 4 ++-- dashboard-ui/bower_components/polymer/.bower.json | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/dashboard-ui/bower_components/emby-webcomponents/.bower.json b/dashboard-ui/bower_components/emby-webcomponents/.bower.json index c1ea36e456..7d665c0750 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.70", - "_release": "1.4.70", + "version": "1.4.71", + "_release": "1.4.71", "_resolution": { "type": "version", - "tag": "1.4.70", - "commit": "d1263e18e331a87300ac4580536b26931cea87c9" + "tag": "1.4.71", + "commit": "0613abc5976a0da9db22854809449e09517ae2b6" }, "_source": "https://github.com/MediaBrowser/emby-webcomponents.git", "_target": "^1.2.0", diff --git a/dashboard-ui/bower_components/iron-selector/.bower.json b/dashboard-ui/bower_components/iron-selector/.bower.json index 955c9dc566..01c5a1084b 100644 --- a/dashboard-ui/bower_components/iron-selector/.bower.json +++ b/dashboard-ui/bower_components/iron-selector/.bower.json @@ -36,7 +36,7 @@ "tag": "v1.5.2", "commit": "18e8e12dcd9a4560de480562f65935feed334b86" }, - "_source": "git://github.com/PolymerElements/iron-selector.git", + "_source": "git://github.com/polymerelements/iron-selector.git", "_target": "^1.0.0", - "_originalSource": "PolymerElements/iron-selector" + "_originalSource": "polymerelements/iron-selector" } \ No newline at end of file diff --git a/dashboard-ui/bower_components/polymer/.bower.json b/dashboard-ui/bower_components/polymer/.bower.json index 5320b592cc..cff87fd184 100644 --- a/dashboard-ui/bower_components/polymer/.bower.json +++ b/dashboard-ui/bower_components/polymer/.bower.json @@ -39,6 +39,6 @@ "commit": "8715c83bf04a228de00ec662ed43eb6141e61b91" }, "_source": "git://github.com/Polymer/polymer.git", - "_target": "^1.1.0", + "_target": "^1.2.0", "_originalSource": "Polymer/polymer" } \ No newline at end of file