diff --git a/dashboard-ui/bower_components/emby-webcomponents/.bower.json b/dashboard-ui/bower_components/emby-webcomponents/.bower.json index ade6bfe7c..87bf17215 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/.bower.json +++ b/dashboard-ui/bower_components/emby-webcomponents/.bower.json @@ -16,12 +16,12 @@ }, "devDependencies": {}, "ignore": [], - "version": "1.3.27", - "_release": "1.3.27", + "version": "1.3.37", + "_release": "1.3.37", "_resolution": { "type": "version", - "tag": "1.3.27", - "commit": "d50eb817e00a23afb8fd5c5fe5a08745de291951" + "tag": "1.3.37", + "commit": "43c74d495f1fbdae6d86ac6608e934d396069806" }, "_source": "https://github.com/MediaBrowser/emby-webcomponents.git", "_target": "^1.2.0", diff --git a/dashboard-ui/bower_components/emby-webcomponents/focusmanager.js b/dashboard-ui/bower_components/emby-webcomponents/focusmanager.js index 055db22dc..6bf5e4a1c 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/focusmanager.js +++ b/dashboard-ui/bower_components/emby-webcomponents/focusmanager.js @@ -305,14 +305,8 @@ define([], function () { return intersectsInternal(a1, a2, b1, b2) || intersectsInternal(b1, b2, a1, a2); } - var enableDebugInfo = false; - function getNearestElements(elementInfos, options, direction) { - if (enableDebugInfo) { - removeAll(); - } - // Get elements and work out x/y points var cache = [], point1x = parseFloat(options.left) || 0, @@ -379,10 +373,6 @@ define([], function () { break; } - if (enableDebugInfo) { - addDebugInfo(elem, distX, distY); - } - var distT = Math.sqrt(distX * distX + distY * distY); var distT2 = Math.sqrt(distX2 * distX2 + distY2 * distY2); @@ -405,36 +395,6 @@ define([], function () { return cache; } - function addDebugInfo(elem, distX, distY) { - - var div = elem.querySelector('focusInfo'); - - if (!div) { - div = document.createElement('div'); - div.classList.add('focusInfo'); - elem.appendChild(div); - - if (getComputedStyle(elem, null).getPropertyValue('position') == 'static') { - elem.style.position = 'relative'; - } - div.style.position = 'absolute'; - div.style.left = '0'; - div.style.top = '0'; - div.style.color = 'white'; - div.style.backgroundColor = 'red'; - div.style.padding = '2px'; - } - - div.innerHTML = Math.round(distX) + ',' + Math.round(distY); - } - - function removeAll() { - var elems = document.querySelectorAll('.focusInfo'); - for (var i = 0, length = elems.length; i < length; i++) { - elems[i].parentNode.removeChild(elems[i]); - } - } - function sortNodesX(a, b) { var result = a.distX - b.distX; diff --git a/dashboard-ui/bower_components/emby-webcomponents/formdialog.css b/dashboard-ui/bower_components/emby-webcomponents/formdialog.css index e87ed7f43..e78a4c73d 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/formdialog.css +++ b/dashboard-ui/bower_components/emby-webcomponents/formdialog.css @@ -15,7 +15,7 @@ margin-left: .75em; } - .formDialog form { + .formDialog .centeredContent { margin: 0 auto; } @@ -25,16 +25,19 @@ .formDialog .dialogContentInner { padding-bottom: 10vh; - padding-top: 1em; } .layout-tv .formDialog .dialogContentInner { padding-bottom: 50vh; } +.formDialog .centeredContent { + max-width: 700px; +} + @media all and (min-width: 1000px) { - .formDialog form { + .layout-tv .formDialog .centeredContent { max-width: 70vw; } } diff --git a/dashboard-ui/bower_components/emby-webcomponents/guide/guide.js b/dashboard-ui/bower_components/emby-webcomponents/guide/guide.js index 19a2c103a..2e1cac707 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/guide/guide.js +++ b/dashboard-ui/bower_components/emby-webcomponents/guide/guide.js @@ -1,4 +1,4 @@ -define(['require', 'globalize', 'connectionManager', 'loading', 'scrollHelper', 'datetime', 'focusManager', 'imageLoader', 'events', 'layoutManager', 'itemShortcuts', 'registrationservices', 'clearButtonStyle', 'css!./guide.css', 'html!./../icons/mediainfo.html', 'html!./../icons/nav.html', 'scrollStyles'], function (require, globalize, connectionManager, loading, scrollHelper, datetime, focusManager, imageLoader, events, layoutManager, itemShortcuts, registrationServices) { +define(['require', 'browser', 'globalize', 'connectionManager', 'loading', 'scrollHelper', 'datetime', 'focusManager', 'imageLoader', 'events', 'layoutManager', 'itemShortcuts', 'registrationservices', 'clearButtonStyle', 'css!./guide.css', 'html!./../icons/mediainfo.html', 'html!./../icons/nav.html', 'scrollStyles'], function (require, browser, globalize, connectionManager, loading, scrollHelper, datetime, focusManager, imageLoader, events, layoutManager, itemShortcuts, registrationServices) { function Guide(options) { @@ -59,7 +59,7 @@ return registrationServices.validateFeature('livetv').then(function () { - var limit = 400; + var limit = browser.mobile ? 100 : 400; context.querySelector('.guideRequiresUnlock').classList.add('hide'); diff --git a/dashboard-ui/bower_components/emby-webcomponents/images/imagehelper.js b/dashboard-ui/bower_components/emby-webcomponents/images/imagehelper.js index ac7066053..f97020645 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/images/imagehelper.js +++ b/dashboard-ui/bower_components/emby-webcomponents/images/imagehelper.js @@ -2,6 +2,7 @@ define(['visibleinviewport', 'imageFetcher', 'layoutManager', 'events'], functio var thresholdX; var thresholdY; + var windowSize; function resetThresholds() { @@ -15,25 +16,36 @@ define(['visibleinviewport', 'imageFetcher', 'layoutManager', 'events'], functio thresholdX = x; thresholdY = y; + resetWindowSize(); } - resetThresholds(); - window.addEventListener("orientationchange", resetThresholds); + window.addEventListener('resize', resetThresholds); events.on(layoutManager, 'modechange', resetThresholds); var wheelEvent = (document.implementation.hasFeature('Event.wheel', '3.0') ? 'wheel' : 'mousewheel'); - function isVisible(elem, windowSize) { + function resetWindowSize() { + windowSize = { + innerHeight: window.innerHeight, + innerWidth: window.innerWidth + }; + } + resetThresholds(); + + function isVisible(elem) { return visibleinviewport(elem, true, thresholdX, thresholdY, windowSize); } var self = {}; - function fillImage(elem) { - var source = elem.getAttribute('data-src'); + function fillImage(elem, source, enableEffects) { + + if (!source) { + source = elem.getAttribute('data-src'); + } if (source) { - if (self.enableFade) { + if (self.enableFade && enableEffects !== false) { imageFetcher.loadImage(elem, source).then(fadeIn); } else { imageFetcher.loadImage(elem, source); @@ -44,10 +56,6 @@ define(['visibleinviewport', 'imageFetcher', 'layoutManager', 'events'], functio function fadeIn(elem) { - if (elem.classList.contains('noFade')) { - return; - } - var keyframes = [ { opacity: '0', offset: 0 }, { opacity: '1', offset: 1 }]; @@ -93,11 +101,6 @@ define(['visibleinviewport', 'imageFetcher', 'layoutManager', 'events'], functio var anyFound = false; var out = false; - var windowSize = { - innerHeight: window.innerHeight, - innerWidth: window.innerWidth - }; - // TODO: This out construct assumes left to right, top to bottom for (var i = 0, length = images.length; i < length; i++) { @@ -106,7 +109,7 @@ define(['visibleinviewport', 'imageFetcher', 'layoutManager', 'events'], functio return; } var img = images[i]; - if (!out && isVisible(img, windowSize)) { + if (!out && isVisible(img)) { anyFound = true; fillImage(img); } else { @@ -116,6 +119,10 @@ define(['visibleinviewport', 'imageFetcher', 'layoutManager', 'events'], functio } remaining.push(img); } + + if (out) { + return; + } } images = remaining; @@ -157,29 +164,11 @@ define(['visibleinviewport', 'imageFetcher', 'layoutManager', 'events'], functio unveil(); } - function fillImages(elems) { - - for (var i = 0, length = elems.length; i < length; i++) { - var elem = elems[0]; - var source = elem.getAttribute('data-src'); - if (source) { - ImageStore.setImageInto(elem, source); - elem.setAttribute("data-src", ''); - } - } - } - function lazyChildren(elem) { unveilElements(elem.getElementsByClassName('lazy')); } - function lazyImage(elem, url) { - - elem.setAttribute('data-src', url); - fillImages([elem]); - } - function getPrimaryImageAspectRatio(items) { var values = []; @@ -245,14 +234,8 @@ define(['visibleinviewport', 'imageFetcher', 'layoutManager', 'events'], functio } } - function lazyImage(elem, url) { - - elem.setAttribute('data-src', url); - fillImage(elem); - } - self.fillImages = fillImages; - self.lazyImage = lazyImage; + self.lazyImage = fillImage; self.lazyChildren = lazyChildren; self.getPrimaryImageAspectRatio = getPrimaryImageAspectRatio; diff --git a/dashboard-ui/bower_components/emby-webcomponents/loading/loading-lite.js b/dashboard-ui/bower_components/emby-webcomponents/loading/loading-lite.js index f7bdc50b3..a39bc0fa7 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/loading/loading-lite.js +++ b/dashboard-ui/bower_components/emby-webcomponents/loading/loading-lite.js @@ -1,12 +1,16 @@ define(['MaterialSpinner', 'css!./loading'], function () { + var loadingElem; + return { show: function () { - var elem = document.querySelector('.docspinner'); + var elem = loadingElem; if (!elem) { elem = document.createElement("div"); + loadingElem = elem; + elem.classList.add('docspinner'); elem.classList.add('mdl-spinner'); elem.classList.add('mdl-js-spinner'); @@ -19,7 +23,7 @@ define(['MaterialSpinner', 'css!./loading'], function () { elem.classList.remove('loadingHide'); }, hide: function () { - var elem = document.querySelector('.docspinner'); + var elem = loadingElem; if (elem) { diff --git a/dashboard-ui/bower_components/emby-webcomponents/mediainfo/mediainfo.js b/dashboard-ui/bower_components/emby-webcomponents/mediainfo/mediainfo.js index 3cebeefea..9d3172794 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/mediainfo/mediainfo.js +++ b/dashboard-ui/bower_components/emby-webcomponents/mediainfo/mediainfo.js @@ -360,9 +360,11 @@ define(['datetime', 'globalize', 'embyRouter', 'html!./../icons/mediainfo.html', function afterFill(elem, item, options) { - var endsAtElem = elem.querySelector('.endsAt'); - if (endsAtElem) { - dynamicEndTime(endsAtElem, item); + if (options.endsAt !== false) { + var endsAtElem = elem.querySelector('.endsAt'); + if (endsAtElem) { + dynamicEndTime(endsAtElem, item); + } } var lnkChannel = elem.querySelector('.lnkChannel'); diff --git a/dashboard-ui/bower_components/emby-webcomponents/recordingcreator/recordingcreator.css b/dashboard-ui/bower_components/emby-webcomponents/recordingcreator/recordingcreator.css index 842bfe0ff..036c0f1d8 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/recordingcreator/recordingcreator.css +++ b/dashboard-ui/bower_components/emby-webcomponents/recordingcreator/recordingcreator.css @@ -1,3 +1,7 @@ .recordingDialog .btnSubmit { background-color: #cc3333; -} \ No newline at end of file +} + +.layout-tv .btnHeaderSave { + display: none; +} diff --git a/dashboard-ui/bower_components/emby-webcomponents/recordingcreator/recordingcreator.js b/dashboard-ui/bower_components/emby-webcomponents/recordingcreator/recordingcreator.js index acbd4b581..782b3fbd8 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/recordingcreator/recordingcreator.js +++ b/dashboard-ui/bower_components/emby-webcomponents/recordingcreator/recordingcreator.js @@ -388,7 +388,7 @@ if (recordingCreated) { require(['toast'], function (toast) { - toast(globalize.translate('RecordingScheduled')); + toast(globalize.translate('sharedcomponents#RecordingScheduled')); }); resolve(); } else { diff --git a/dashboard-ui/bower_components/emby-webcomponents/recordingcreator/recordingcreator.template.html b/dashboard-ui/bower_components/emby-webcomponents/recordingcreator/recordingcreator.template.html index 855a35613..c2d168064 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/recordingcreator/recordingcreator.template.html +++ b/dashboard-ui/bower_components/emby-webcomponents/recordingcreator/recordingcreator.template.html @@ -5,7 +5,7 @@
-
+

@@ -52,11 +52,11 @@
- +

- +

diff --git a/dashboard-ui/bower_components/emby-webcomponents/recordingcreator/recordingeditor.js b/dashboard-ui/bower_components/emby-webcomponents/recordingcreator/recordingeditor.js new file mode 100644 index 000000000..34b469c09 --- /dev/null +++ b/dashboard-ui/bower_components/emby-webcomponents/recordingcreator/recordingeditor.js @@ -0,0 +1,175 @@ +define(['dialogHelper', 'globalize', 'layoutManager', 'mediaInfo', 'apphost', 'connectionManager', 'require', 'loading', 'scrollHelper', 'scrollStyles', 'paper-checkbox', 'emby-collapsible', 'paper-input', 'paper-icon-button-light', 'css!./../formdialog', 'css!./recordingcreator', 'html!./../icons/mediainfo.html', 'html!./../icons/nav.html'], function (dialogHelper, globalize, layoutManager, mediaInfo, appHost, connectionManager, require, loading, scrollHelper) { + + var currentDialog; + var recordingUpdated = false; + var currentItemId; + var currentServerId; + + function renderTimer(context, item) { + + var programInfo = item.ProgramInfo || {}; + + context.querySelector('.itemName').innerHTML = item.Name; + context.querySelector('.itemEpisodeName').innerHTML = programInfo.EpisodeTitle || ''; + + context.querySelector('.itemGenres').innerHTML = (programInfo.Genres || []).join(' / '); + context.querySelector('.itemOverview').innerHTML = programInfo.Overview || ''; + + var timerPageImageContainer = context.querySelector('.timerPageImageContainer'); + + context.querySelector('.itemMiscInfoPrimary').innerHTML = mediaInfo.getPrimaryMediaInfoHtml(programInfo); + context.querySelector('.itemMiscInfoSecondary').innerHTML = mediaInfo.getSecondaryMediaInfoHtml(programInfo); + + context.querySelector('#txtPrePaddingMinutes').value = item.PrePaddingSeconds / 60; + context.querySelector('#txtPostPaddingMinutes').value = item.PostPaddingSeconds / 60; + + var timerStausElem = context.querySelector('.timerStatus'); + + if (item.Status == 'New') { + timerStausElem.classList.add('hide'); + } else { + timerStausElem.classList.remove('hide'); + timerStausElem.innerHTML = 'Status:   ' + item.Status; + } + + loading.hide(); + } + + function closeDialog(isSubmitted) { + + recordingUpdated = isSubmitted; + dialogHelper.close(currentDialog); + } + + function onSubmit(e) { + + loading.show(); + + var form = this; + + var apiClient = connectionManager.getApiClient(currentServerId); + + apiClient.getLiveTvTimer(currentItemId).then(function (item) { + + item.PrePaddingSeconds = form.querySelector('#txtPrePaddingMinutes').value * 60; + item.PostPaddingSeconds = form.querySelector('#txtPostPaddingMinutes').value * 60; + apiClient.updateLiveTvTimer(item).then(function () { + loading.hide(); + require(['toast'], function (toast) { + toast(Globalize.translate('MessageRecordingSaved')); + closeDialog(true); + }); + }); + }); + + e.preventDefault(); + + // Disable default form submission + return false; + } + + function init(context) { + + context.querySelector('.btnCancel').addEventListener('click', function () { + + closeDialog(false); + }); + + context.querySelector('form').addEventListener('submit', onSubmit); + + context.querySelector('.btnHeaderSave').addEventListener('click', function (e) { + + context.querySelector('.btnSubmit').click(); + }); + + context.querySelector('.btnSubmit').addEventListener('click', function () { + + // Do a fake form submit this the button isn't a real submit button + var fakeSubmit = document.createElement('input'); + fakeSubmit.setAttribute('type', 'submit'); + fakeSubmit.style.display = 'none'; + var form = context.querySelector('form'); + form.appendChild(fakeSubmit); + fakeSubmit.click(); + + // Seeing issues in smart tv browsers where the form does not get submitted if the button is removed prior to the submission actually happening + setTimeout(function () { + form.removeChild(fakeSubmit); + }, 500); + }); + } + + function reload(context, id) { + + loading.show(); + currentItemId = id; + + var apiClient = connectionManager.getApiClient(currentServerId); + apiClient.getLiveTvTimer(id).then(function (result) { + + renderTimer(context, result); + loading.hide(); + }); + } + + function showEditor(itemId, serverId) { + + return new Promise(function (resolve, reject) { + + recordingUpdated = false; + currentServerId = serverId; + loading.show(); + + require(['text!./recordingeditor.template.html'], function (template) { + + var dialogOptions = { + removeOnClose: true, + scrollY: false + }; + + if (layoutManager.tv) { + dialogOptions.size = 'fullscreen'; + } else { + dialogOptions.size = 'small'; + } + + var dlg = dialogHelper.createDialog(dialogOptions); + + dlg.classList.add('formDialog'); + dlg.classList.add('recordingDialog'); + + var html = ''; + + html += globalize.translateDocument(template, 'sharedcomponents'); + + dlg.innerHTML = html; + document.body.appendChild(dlg); + + currentDialog = dlg; + + dlg.addEventListener('close', function () { + + if (recordingUpdated) { + resolve(); + } else { + reject(); + } + }); + + if (layoutManager.tv) { + scrollHelper.centerFocus.on(dlg.querySelector('.dialogContent'), false); + } + + init(dlg); + + reload(dlg, itemId); + + dialogHelper.open(dlg); + }); + }); + } + + return { + show: showEditor + }; +}); \ No newline at end of file diff --git a/dashboard-ui/bower_components/emby-webcomponents/recordingcreator/recordingeditor.template.html b/dashboard-ui/bower_components/emby-webcomponents/recordingcreator/recordingeditor.template.html new file mode 100644 index 000000000..0ef855f60 --- /dev/null +++ b/dashboard-ui/bower_components/emby-webcomponents/recordingcreator/recordingeditor.template.html @@ -0,0 +1,43 @@ +
+ +
+ ${Edit} +
+
+ + + ${Save} + +
+
+ +
+
+

+

+

+

+

+

+ +

+

+ + +
+
+ +
+
+
+ +
+
+
+ ${Save} +
+
+
+ +
+
\ No newline at end of file diff --git a/dashboard-ui/bower_components/emby-webcomponents/strings/en-US.json b/dashboard-ui/bower_components/emby-webcomponents/strings/en-US.json index e21da0381..3b2082f16 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/strings/en-US.json +++ b/dashboard-ui/bower_components/emby-webcomponents/strings/en-US.json @@ -34,8 +34,8 @@ "Saturday": "Saturday", "Days": "Days", "RecordSeries": "Record series", - "PrePaddingMinutes": "Pre-padding minutes:", - "PostPaddingMinutes": "Post-padding minutes:", + "LabelPrePaddingMinutes": "Pre-padding minutes:", + "LabelPostPaddingMinutes": "Post-padding minutes:", "RecordOnAllChannels": "Record on all channels", "RecordAnytime": "Record at any time", "RecordOnlyNewEpisodes": "Record only new episodes", @@ -46,6 +46,8 @@ "OptionConvertRecordingsToStreamingFormatHelp": "Recordings will be converted on the fly to MP4 for easy playback on your devices.", "FeatureRequiresEmbyPremiere": "This feature requires an active Emby Premiere subscription.", "Record": "Record", + "Save": "Save", + "Edit": "Edit", "Download": "Download", "Advanced": "Advanced", "Refresh": "Refresh", diff --git a/dashboard-ui/bower_components/emby-webcomponents/viewmanager/viewcontainer-lite.js b/dashboard-ui/bower_components/emby-webcomponents/viewmanager/viewcontainer-lite.js index a3d1ffe2d..40a97b1e9 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/viewmanager/viewcontainer-lite.js +++ b/dashboard-ui/bower_components/emby-webcomponents/viewmanager/viewcontainer-lite.js @@ -1,9 +1,11 @@ define(['browser'], function (browser) { var allPages = document.querySelectorAll('.mainAnimatedPage'); + var currentUrls = []; var pageContainerCount = allPages.length; var animationDuration = 500; var allowAnimation = true; + var selectedPageIndex = -1; function enableAnimation() { @@ -33,9 +35,12 @@ define(['browser'], function (browser) { pageIndex = 0; } - var html = '
'; - html += options.view; - html += '
'; + var view = document.createElement('div'); + view.classList.add('page-view'); + if (options.type) { + view.setAttribute('data-type', options.type); + } + view.innerHTML = options.view; var animatable = allPages[pageIndex]; @@ -43,12 +48,11 @@ define(['browser'], function (browser) { if (currentPage) { triggerDestroy(currentPage); + animatable.replaceChild(view, currentPage); + } else { + animatable.appendChild(view); } - animatable.innerHTML = html; - - var view = animatable.querySelector('.page-view'); - if (onBeforeChange) { onBeforeChange(view, false, options); } @@ -58,6 +62,8 @@ define(['browser'], function (browser) { // animate here return animate(animatable, previousAnimatable, options.transition, options.isBack).then(function () { + selectedPageIndex = pageIndex; + currentUrls[pageIndex] = options.url; if (!options.cancel && previousAnimatable) { afterAnimate(allPages, pageIndex); } @@ -122,7 +128,7 @@ define(['browser'], function (browser) { animations.push(oldAnimatedPage.animate([ { transform: 'none', offset: 0 }, - { transform: 'translateX(' + destination + ')', offset: 1 } + { transform: 'translate3d(' + destination + ', 0, 0)', offset: 1 } ], timings)); } @@ -133,7 +139,7 @@ define(['browser'], function (browser) { animations.push(newAnimatedPage.animate([ - { transform: 'translateX(' + start + ')', offset: 0 }, + { transform: 'translate3d(' + start + ', 0, 0)', offset: 0 }, { transform: 'none', offset: 1 } ], timings)); @@ -215,32 +221,20 @@ define(['browser'], function (browser) { } function getSelectedIndex(allPages) { - for (var i = 0, length = allPages.length; i < length; i++) { - if (!allPages[i].classList.contains('hide')) { - return i; - } - } - return -1; + return selectedPageIndex; } function tryRestoreView(options) { + var url = options.url; - var view = document.querySelector(".page-view[data-url='" + url + "']"); - var page = parentWithClass(view, 'mainAnimatedPage'); + var index = currentUrls.indexOf(url); - if (view) { + if (index != -1) { + var page = allPages[index]; + var view = page.querySelector(".page-view"); - var index = -1; - var pages = document.querySelectorAll('.mainAnimatedPage'); - for (var i = 0, length = pages.length; i < length; i++) { - if (pages[i] == page) { - index = i; - break; - } - } - - if (index != -1) { + if (view) { if (options.cancel) { return; @@ -251,7 +245,6 @@ define(['browser'], function (browser) { var animatable = allPages[index]; var selected = getSelectedIndex(allPages); var previousAnimatable = selected == -1 ? null : allPages[selected]; - var view = animatable.querySelector('.page-view'); if (onBeforeChange) { onBeforeChange(view, true, options); @@ -261,6 +254,7 @@ define(['browser'], function (browser) { return animate(animatable, previousAnimatable, options.transition, options.isBack).then(function () { + selectedPageIndex = index; if (!options.cancel && previousAnimatable) { afterAnimate(allPages, index); } @@ -278,14 +272,7 @@ define(['browser'], function (browser) { function reset() { - var views = document.querySelectorAll(".mainAnimatedPage.hide .page-view"); - - for (var i = 0, length = views.length; i < length; i++) { - - var view = views[i]; - triggerDestroy(view); - view.parentNode.removeChild(view); - } + currentUrls = []; } function parentWithClass(elem, className) { diff --git a/dashboard-ui/bower_components/polymer/.bower.json b/dashboard-ui/bower_components/polymer/.bower.json index bbad7fe7b..3e8a4009a 100644 --- a/dashboard-ui/bower_components/polymer/.bower.json +++ b/dashboard-ui/bower_components/polymer/.bower.json @@ -34,6 +34,6 @@ "commit": "11c987b2eb3c73b388a79fc8aaea8ca01624f514" }, "_source": "git://github.com/Polymer/polymer.git", - "_target": "^1.1.0", + "_target": "^1.0.0", "_originalSource": "Polymer/polymer" } \ No newline at end of file diff --git a/dashboard-ui/components/recordingeditor/recordingeditor.js b/dashboard-ui/components/recordingeditor/recordingeditor.js deleted file mode 100644 index 11eccea95..000000000 --- a/dashboard-ui/components/recordingeditor/recordingeditor.js +++ /dev/null @@ -1,163 +0,0 @@ -define(['dialogHelper', 'loading', 'jQuery', 'mediaInfo', 'paper-checkbox', 'paper-input', 'emby-collapsible', 'paper-button', 'paper-icon-button-light'], function (dialogHelper, loading, $, mediaInfo) { - - var currentDialog; - var recordingUpdated = false; - var currentItemId; - - function renderTimer(context, item) { - - var programInfo = item.ProgramInfo || {}; - - $('.itemName', context).html(item.Name); - - $('.itemEpisodeName', context).html(programInfo.EpisodeTitle || ''); - - LibraryBrowser.renderGenres($('.itemGenres', context), programInfo); - LibraryBrowser.renderOverview(context.querySelectorAll('.itemOverview'), programInfo); - - if (programInfo.ImageTags && programInfo.ImageTags.Primary) { - - var imgUrl = ApiClient.getScaledImageUrl(programInfo.Id, { - maxWidth: 200, - maxHeight: 200, - tag: programInfo.ImageTags.Primary, - type: "Primary" - }); - - $('.timerPageImageContainer', context).css("display", "inline-block") - .html(''); - - } else { - $('.timerPageImageContainer', context).hide(); - } - - $('.itemMiscInfoPrimary', context).html(mediaInfo.getPrimaryMediaInfoHtml(programInfo)); - $('.itemMiscInfoSecondary', context).html(mediaInfo.getSecondaryMediaInfoHtml(programInfo)); - - $('#txtPrePaddingMinutes', context).val(item.PrePaddingSeconds / 60); - $('#txtPostPaddingMinutes', context).val(item.PostPaddingSeconds / 60); - - if (item.Status == 'New') { - $('.timerStatus', context).hide(); - } else { - $('.timerStatus', context).show().html('Status:   ' + item.Status); - } - - loading.hide(); - } - - function closeDialog(isSubmitted) { - - recordingUpdated = isSubmitted; - dialogHelper.close(currentDialog); - } - - function onSubmit(e) { - - loading.show(); - - var form = this; - - ApiClient.getLiveTvTimer(currentItemId).then(function (item) { - - item.PrePaddingSeconds = $('#txtPrePaddingMinutes', form).val() * 60; - item.PostPaddingSeconds = $('#txtPostPaddingMinutes', form).val() * 60; - ApiClient.updateLiveTvTimer(item).then(function () { - loading.hide(); - require(['toast'], function (toast) { - toast(Globalize.translate('MessageRecordingSaved')); - closeDialog(true); - }); - }); - }); - - e.preventDefault(); - - // Disable default form submission - return false; - } - - function init(context) { - - context.querySelector('.btnCancel').addEventListener('click', function () { - - closeDialog(false); - }); - - context.querySelector('form').addEventListener('submit', onSubmit); - - context.querySelector('.btnHeaderSave').addEventListener('click', function (e) { - - context.querySelector('.btnSave').click(); - }); - } - - function reload(context, id) { - - loading.show(); - currentItemId = id; - - ApiClient.getLiveTvTimer(id).then(function (result) { - - renderTimer(context, result); - loading.hide(); - }); - } - - function showEditor(itemId) { - - return new Promise(function (resolve, reject) { - - recordingUpdated = false; - loading.show(); - - var xhr = new XMLHttpRequest(); - xhr.open('GET', 'components/recordingeditor/recordingeditor.template.html', true); - - xhr.onload = function (e) { - - var template = this.response; - var dlg = dialogHelper.createDialog({ - removeOnClose: true, - size: 'small', - autoFocus: false - }); - - dlg.classList.add('ui-body-b'); - dlg.classList.add('background-theme-b'); - - dlg.classList.add('formDialog'); - - var html = ''; - - html += Globalize.translateDocument(template); - - dlg.innerHTML = html; - document.body.appendChild(dlg); - - dialogHelper.open(dlg); - - currentDialog = dlg; - - dlg.addEventListener('close', function () { - - if (recordingUpdated) { - resolve(); - } else { - reject(); - } - }); - - init(dlg); - - reload(dlg, itemId); - } - - xhr.send(); - }); - } - - return { - show: showEditor - }; -}); \ No newline at end of file diff --git a/dashboard-ui/components/recordingeditor/recordingeditor.template.html b/dashboard-ui/components/recordingeditor/recordingeditor.template.html deleted file mode 100644 index 87a55ce20..000000000 --- a/dashboard-ui/components/recordingeditor/recordingeditor.template.html +++ /dev/null @@ -1,48 +0,0 @@ -
- -
- ${ButtonEdit} -
-
- - - ${ButtonSave} - -
-
- -
-
- -
-

-

-

-

-

-
-
-

- -

-

-
-
-
-
- -
-
-
- -
-
-
- -
-
-
-
\ No newline at end of file diff --git a/dashboard-ui/components/viewcontainer-lite.js b/dashboard-ui/components/viewcontainer-lite.js index 5f7fe0379..dd70c1d2c 100644 --- a/dashboard-ui/components/viewcontainer-lite.js +++ b/dashboard-ui/components/viewcontainer-lite.js @@ -1,49 +1,65 @@ -define(['jQuery'], function ($) { +define(['browser'], function (browser) { - var pageContainerCount; + var allPages = document.querySelectorAll('.mainAnimatedPage'); + var pageContainerCount = allPages.length; var animationDuration = 500; + var allowAnimation = true; + var selectedPageIndex = -1; + + function enableAnimation() { + + if (!allowAnimation) { + return false; + } + if (browser.tv) { + return false; + } + + return true; + } function loadView(options) { + if (options.cancel) { + return; + } + + cancelActiveAnimations(); + + var selected = getSelectedIndex(allPages); + var previousAnimatable = selected == -1 ? null : allPages[selected]; + var pageIndex = selected + 1; + + if (pageIndex >= pageContainerCount) { + pageIndex = 0; + } + + var newViewInfo = normalizeNewView(options); + var newView = newViewInfo.elem; + + var dependencies = typeof (newView) == 'string' ? null : newView.getAttribute('data-require'); + dependencies = dependencies ? dependencies.split(',') : []; + + var isPluginpage = options.url.toLowerCase().indexOf('/configurationpage?') != -1; + + if (isPluginpage) { + dependencies.push('jqmpopup'); + dependencies.push('jqmcollapsible'); + dependencies.push('jqmcheckbox'); + dependencies.push('legacy/dashboard'); + dependencies.push('legacy/selectmenu'); + dependencies.push('jqmcontrolgroup'); + } + + if (isPluginpage || (newView.classList && newView.classList.contains('type-interior'))) { + dependencies.push('jqmlistview'); + dependencies.push('scripts/notifications'); + } + return new Promise(function (resolve, reject) { - var animatedPages = document.querySelector('.mainAnimatedPages'); - - if (options.cancel) { - return; - } - - var selected = getSelectedIndex(animatedPages); - var pageIndex = selected + 1; - - if (pageIndex >= pageContainerCount) { - pageIndex = 0; - } - - var newViewInfo = normalizeNewView(options); - var newView = newViewInfo.elem; - - var dependencies = typeof (newView) == 'string' ? null : newView.getAttribute('data-require'); - dependencies = dependencies ? dependencies.split(',') : []; - - var isPluginpage = options.url.toLowerCase().indexOf('/configurationpage?') != -1; - - if (isPluginpage) { - dependencies.push('jqmpopup'); - dependencies.push('jqmcollapsible'); - dependencies.push('jqmcheckbox'); - dependencies.push('legacy/dashboard'); - dependencies.push('legacy/selectmenu'); - dependencies.push('jqmcontrolgroup'); - } - - if (isPluginpage || (newView.classList && newView.classList.contains('type-interior'))) { - dependencies.push('jqmlistview'); - dependencies.push('scripts/notifications'); - } - require(dependencies, function () { - var allPages = animatedPages.querySelectorAll('.mainAnimatedPage'); + var animatable = allPages[pageIndex]; var currentPage = animatable.querySelector('.page-view'); @@ -52,37 +68,45 @@ define(['jQuery'], function ($) { triggerDestroy(currentPage); } - for (var i = 0, length = allPages.length; i < length; i++) { - if (pageIndex == i) { - allPages[i].classList.remove('hide'); - } else { - allPages[i].classList.add('hide'); - } - } + var view; if (typeof (newView) == 'string') { animatable.innerHTML = newView; + view = animatable.querySelector('.page-view'); } else { - animatable.innerHTML = ''; if (newViewInfo.hasScript) { // TODO: figure this out without jQuery + animatable.innerHTML = ''; $(newView).appendTo(animatable); } else { - animatable.appendChild(newView); + if (currentPage) { + animatable.replaceChild(newView, currentPage); + } else { + animatable.appendChild(newView); + } } enhanceNewView(dependencies, newView); + view = newView; } - var view = animatable.querySelector('.page-view'); - if (onBeforeChange) { onBeforeChange(view, false, options); } - $.mobile = $.mobile || {}; - $.mobile.activePage = view; + beforeAnimate(allPages, pageIndex, selected); + // animate here + animate(animatable, previousAnimatable, options.transition, options.isBack).then(function () { - resolve(view); + selectedPageIndex = pageIndex; + if (!options.cancel && previousAnimatable) { + afterAnimate(allPages, pageIndex); + } + + $.mobile = $.mobile || {}; + $.mobile.activePage = view; + + resolve(view); + }); }); }); } @@ -141,86 +165,210 @@ define(['jQuery'], function ($) { }; } + function beforeAnimate(allPages, newPageIndex, oldPageIndex) { + for (var i = 0, length = allPages.length; i < length; i++) { + if (newPageIndex == i || oldPageIndex == i) { + //allPages[i].classList.remove('hide'); + } else { + allPages[i].classList.add('hide'); + } + } + } + + function afterAnimate(allPages, newPageIndex) { + for (var i = 0, length = allPages.length; i < length; i++) { + if (newPageIndex == i) { + //allPages[i].classList.remove('hide'); + } else { + allPages[i].classList.add('hide'); + } + } + } + + function animate(newAnimatedPage, oldAnimatedPage, transition, isBack) { + + if (enableAnimation() && newAnimatedPage.animate) { + if (transition == 'slide') { + return slide(newAnimatedPage, oldAnimatedPage, transition, isBack); + } else if (transition == 'fade') { + return fade(newAnimatedPage, oldAnimatedPage, transition, isBack); + } + } + + return nullAnimation(newAnimatedPage, oldAnimatedPage, transition, isBack); + } + + function nullAnimation(newAnimatedPage, oldAnimatedPage, transition, isBack) { + + newAnimatedPage.classList.remove('hide'); + return Promise.resolve(); + } + + function slide(newAnimatedPage, oldAnimatedPage, transition, isBack) { + + var timings = { + duration: 450, + iterations: 1, + easing: 'ease-out', + fill: 'both' + } + + var animations = []; + + if (oldAnimatedPage) { + var destination = isBack ? '100%' : '-100%'; + + animations.push(oldAnimatedPage.animate([ + + { transform: 'none', offset: 0 }, + { transform: 'translate3d(' + destination + ', 0, 0)', offset: 1 } + + ], timings)); + } + + newAnimatedPage.classList.remove('hide'); + + var start = isBack ? '-100%' : '100%'; + + animations.push(newAnimatedPage.animate([ + + { transform: 'translate3d(' + start + ', 0, 0)', offset: 0 }, + { transform: 'none', offset: 1 } + + ], timings)); + + currentAnimations = animations; + + return new Promise(function (resolve, reject) { + animations[animations.length - 1].onfinish = resolve; + }); + } + + function fade(newAnimatedPage, oldAnimatedPage, transition, isBack) { + + var timings = { + duration: animationDuration, + iterations: 1, + easing: 'ease-out', + fill: 'both' + } + + var animations = []; + + if (oldAnimatedPage) { + animations.push(oldAnimatedPage.animate([ + + { opacity: 1, offset: 0 }, + { opacity: 0, offset: 1 } + + ], timings)); + } + + newAnimatedPage.classList.remove('hide'); + + animations.push(newAnimatedPage.animate([ + + { opacity: 0, offset: 0 }, + { opacity: 1, offset: 1 } + + ], timings)); + + currentAnimations = animations; + + return new Promise(function (resolve, reject) { + animations[animations.length - 1].onfinish = resolve; + }); + } + + var currentAnimations = []; + function cancelActiveAnimations() { + + var animations = currentAnimations; + for (var i = 0, length = animations.length; i < length; i++) { + cancelAnimation(animations[i]); + } + } + + function cancelAnimation(animation) { + + try { + animation.cancel(); + } catch (err) { + console.log('Error canceling animation: ' + err); + } + } + var onBeforeChange; function setOnBeforeChange(fn) { onBeforeChange = fn; } - function getSelectedIndex(animatedPages) { - var allPages = animatedPages.querySelectorAll('.mainAnimatedPage'); - for (var i = 0, length = allPages.length; i < length; i++) { - if (!allPages[i].classList.contains('hide')) { - return i; - } - } + function sendResolve(resolve, view) { - return -1; + // Don't report completion until the animation has finished, otherwise rendering may not perform well + setTimeout(function () { + + resolve(view); + + }, animationDuration); } - function replaceAnimatedPages() { - var elem = document.querySelector('neon-animated-pages.mainAnimatedPages'); + function getSelectedIndex(allPages) { - if (elem) { - var div = document.createElement('div'); - div.classList.add('mainAnimatedPages'); - div.classList.add('skinBody'); - div.innerHTML = '
'; - elem.parentNode.replaceChild(div, elem); - } - - pageContainerCount = document.querySelectorAll('.mainAnimatedPage').length; + return selectedPageIndex; } function tryRestoreView(options) { - return new Promise(function (resolve, reject) { - var url = options.url; - var view = document.querySelector(".page-view[data-url='" + url + "']"); - var page = parentWithClass(view, 'mainAnimatedPage'); + var url = options.url; + var view = document.querySelector(".page-view[data-url='" + url + "']"); + var page = parentWithClass(view, 'mainAnimatedPage'); - if (view) { + if (view) { - var index = -1; - var pages = document.querySelectorAll('.mainAnimatedPage'); - for (var i = 0, length = pages.length; i < length; i++) { - if (pages[i] == page) { - index = i; - break; - } + var index = -1; + var pages = allPages; + for (var i = 0, length = pages.length; i < length; i++) { + if (pages[i] == page) { + index = i; + break; } - if (index != -1) { + } - var animatedPages = document.querySelector('.mainAnimatedPages'); - if (options.cancel) { - return; - } + if (index != -1) { - var allPages = animatedPages.querySelectorAll('.mainAnimatedPage'); - var animatable = allPages[index]; - var view = animatable.querySelector('.page-view'); + if (options.cancel) { + return; + } - if (onBeforeChange) { - onBeforeChange(view, true, options); - } + cancelActiveAnimations(); - for (var i = 0, length = allPages.length; i < length; i++) { - if (index == i) { - allPages[i].classList.remove('hide'); - } else { - allPages[i].classList.add('hide'); - } + var animatable = allPages[index]; + var selected = getSelectedIndex(allPages); + var previousAnimatable = selected == -1 ? null : allPages[selected]; + + if (onBeforeChange) { + onBeforeChange(view, true, options); + } + + beforeAnimate(allPages, index, selected); + + return animate(animatable, previousAnimatable, options.transition, options.isBack).then(function () { + + selectedPageIndex = index; + if (!options.cancel && previousAnimatable) { + afterAnimate(allPages, index); } $.mobile = $.mobile || {}; $.mobile.activePage = view; - resolve(view); - return; - } + return view; + }); } + } - reject(); - }); + return Promise.reject(); } function triggerDestroy(view) { @@ -252,12 +400,18 @@ define(['jQuery'], function ($) { return elem; } - replaceAnimatedPages(); + function init(isAnimationAllowed) { + + if (allowAnimation && enableAnimation() && !browser.animate) { + require(['webAnimations']); + } + } return { loadView: loadView, tryRestoreView: tryRestoreView, reset: reset, - setOnBeforeChange: setOnBeforeChange + setOnBeforeChange: setOnBeforeChange, + init: init }; }); \ No newline at end of file diff --git a/dashboard-ui/css/librarymenu.css b/dashboard-ui/css/librarymenu.css index 0293642d5..2e87d14e8 100644 --- a/dashboard-ui/css/librarymenu.css +++ b/dashboard-ui/css/librarymenu.css @@ -250,7 +250,7 @@ height: 2px; transition: all .2s cubic-bezier(.4,0,1,1); right: 0; - transform: translateX(-100%); + transform: translate3d(-100%, 0, 0); border-radius: 0; z-index: 1; } diff --git a/dashboard-ui/scripts/itemdetailpage.js b/dashboard-ui/scripts/itemdetailpage.js index 0a4ee9d5e..18db76b4e 100644 --- a/dashboard-ui/scripts/itemdetailpage.js +++ b/dashboard-ui/scripts/itemdetailpage.js @@ -473,7 +473,9 @@ }); }); - LibraryBrowser.renderGenres($('.itemGenres', page), item, null, isStatic); + $('.itemGenres', page).each(function() { + LibraryBrowser.renderGenres(this, item, null, isStatic); + }); LibraryBrowser.renderStudios($('.itemStudios', page), item, isStatic); renderUserDataIcons(page, item); LibraryBrowser.renderLinks(page.querySelector('.itemExternalLinks'), item); diff --git a/dashboard-ui/scripts/librarybrowser.js b/dashboard-ui/scripts/librarybrowser.js index 3e63539d9..31a045328 100644 --- a/dashboard-ui/scripts/librarybrowser.js +++ b/dashboard-ui/scripts/librarybrowser.js @@ -243,7 +243,7 @@ function fadeInRight(elem) { - var pct = browserInfo.mobile ? '1%' : '0.5%'; + var pct = browserInfo.mobile ? '1.5%' : '0.5%'; var keyframes = [ { opacity: '0', transform: 'translate3d(' + pct + ', 0, 0)', offset: 0 }, @@ -793,9 +793,10 @@ editTimer: function (id) { - require(['components/recordingeditor/recordingeditor'], function (recordingeditor) { + require(['recordingEditor'], function (recordingEditor) { - recordingeditor.show(id); + var serverId = ApiClient.serverInfo().Id; + recordingEditor.show(id, serverId); }); }, @@ -3343,7 +3344,7 @@ } } - elem.html(html).trigger('create'); + elem.innerHTML = html; }, renderPremiereDate: function (elem, item) { @@ -3390,9 +3391,8 @@ tag: item.BackdropImageTags[0] }); - itemBackdropElement.classList.add('noFade'); itemBackdropElement.classList.remove('noBackdrop'); - ImageLoader.lazyImage(itemBackdropElement, imgUrl); + ImageLoader.lazyImage(itemBackdropElement, imgUrl, false); hasbackdrop = true; } else if (item.ParentBackdropItemId && item.ParentBackdropImageTags && item.ParentBackdropImageTags.length) { @@ -3404,9 +3404,8 @@ maxWidth: screenWidth }); - itemBackdropElement.classList.add('noFade'); itemBackdropElement.classList.remove('noBackdrop'); - ImageLoader.lazyImage(itemBackdropElement, imgUrl); + ImageLoader.lazyImage(itemBackdropElement, imgUrl, false); hasbackdrop = true; } else { diff --git a/dashboard-ui/scripts/livetvrecordings.js b/dashboard-ui/scripts/livetvrecordings.js index 76d69490d..9e16f3885 100644 --- a/dashboard-ui/scripts/livetvrecordings.js +++ b/dashboard-ui/scripts/livetvrecordings.js @@ -54,6 +54,14 @@ Dashboard.hideLoadingMsg(); } + function getSquareShape() { + return enableScrollX() ? 'overflowSquare' : 'square'; + } + + function enableScrollX() { + return browserInfo.mobile && AppInfo.enableAppLayouts; + } + function renderRecordings(elem, recordings) { if (recordings.length) { @@ -63,9 +71,16 @@ } var recordingItems = elem.querySelector('.recordingItems'); + + if (enableScrollX()) { + recordingItems.classList.add('hiddenScrollX'); + } else { + recordingItems.classList.remove('hiddenScrollX'); + } + recordingItems.innerHTML = LibraryBrowser.getPosterViewHtml({ items: recordings, - shape: "auto", + shape: (enableScrollX() ? getSquareShape() : 'auto'), showTitle: true, showParentTitle: true, centerText: true, diff --git a/dashboard-ui/scripts/site.js b/dashboard-ui/scripts/site.js index fb4b3d079..17bbe19a0 100644 --- a/dashboard-ui/scripts/site.js +++ b/dashboard-ui/scripts/site.js @@ -1822,6 +1822,7 @@ var AppInfo = {}; define("libjass", [bowerPath + "/libjass/libjass", "css!" + bowerPath + "/libjass/libjass"], returnFirstDependency); define("recordingCreator", [embyWebComponentsBowerPath + "/recordingcreator/recordingcreator"], returnFirstDependency); + define("recordingEditor", [embyWebComponentsBowerPath + "/recordingcreator/recordingeditor"], returnFirstDependency); define("mediaInfo", [embyWebComponentsBowerPath + "/mediainfo/mediainfo"], returnFirstDependency); define("backdrop", [embyWebComponentsBowerPath + "/backdrop/backdrop"], returnFirstDependency); define("fetchHelper", [embyWebComponentsBowerPath + "/fetchhelper"], returnFirstDependency); @@ -3102,7 +3103,7 @@ var AppInfo = {}; loadTheme(); if (browserInfo.safari && browserInfo.mobile) { - initFastClick(); + //initFastClick(); } if (Dashboard.isRunningInCordova()) { diff --git a/dashboard-ui/thirdparty/paper-button-style.css b/dashboard-ui/thirdparty/paper-button-style.css index 955861309..5c4609ba0 100644 --- a/dashboard-ui/thirdparty/paper-button-style.css +++ b/dashboard-ui/thirdparty/paper-button-style.css @@ -36,11 +36,11 @@ } paper-button.submit { - color: #4285f4; + color: #52B54B; } paper-button[raised].submit { - background: #4285f4; + background: #52B54B; color: #fff; } @@ -54,11 +54,11 @@ } paper-button.secondary { - color: #52B54B; + color: #4285f4; } paper-button[raised].secondary { - background: #52B54B; + background: #4285f4; color: #fff; }