diff --git a/dashboard-ui/bower_components/emby-webcomponents/.bower.json b/dashboard-ui/bower_components/emby-webcomponents/.bower.json index 270b73a86f..1932b87595 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.2.39", - "_release": "1.2.39", + "version": "1.2.48", + "_release": "1.2.48", "_resolution": { "type": "version", - "tag": "1.2.39", - "commit": "956d151bb5506eea13c56b69f21220b55d2b7d35" + "tag": "1.2.48", + "commit": "342107db8bbaa343a4c61be6fd2591121a83ffff" }, "_source": "https://github.com/MediaBrowser/emby-webcomponents.git", "_target": "^1.2.0", diff --git a/dashboard-ui/bower_components/emby-webcomponents/actionsheet/actionsheet.css b/dashboard-ui/bower_components/emby-webcomponents/actionsheet/actionsheet.css index 762e76a745..6fa8ee1182 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/actionsheet/actionsheet.css +++ b/dashboard-ui/bower_components/emby-webcomponents/actionsheet/actionsheet.css @@ -25,12 +25,17 @@ transition: transform .3s cubic-bezier(.4,0,.2,1),opacity .2s cubic-bezier(.4,0,.2,1); transition: transform .3s cubic-bezier(.4,0,.2,1),opacity .2s cubic-bezier(.4,0,.2,1),-webkit-transform .3s cubic-bezier(.4,0,.2,1); z-index: -1;*/ + max-height: 84%; } - .actionSheet.centered .actionSheetContent { - text-align: center; - align-items: center; - } +.layout-tv .actionSheet { + max-height: none; +} + +.actionSheet.centered .actionSheetContent { + text-align: center; + align-items: center; +} .actionSheetContent { margin: 0 !important; diff --git a/dashboard-ui/bower_components/emby-webcomponents/clearbutton.css b/dashboard-ui/bower_components/emby-webcomponents/clearbutton.css new file mode 100644 index 0000000000..2d3f8d6800 --- /dev/null +++ b/dashboard-ui/bower_components/emby-webcomponents/clearbutton.css @@ -0,0 +1,12 @@ +.clearButton { + background: transparent; + border: 0 !important; + padding: 0 !important; + cursor: pointer; + outline: none !important; + color: inherit; + width: 100%; + vertical-align: middle; + font-family: inherit; + font-size: inherit; +} \ No newline at end of file diff --git a/dashboard-ui/bower_components/emby-webcomponents/guide/guide.css b/dashboard-ui/bower_components/emby-webcomponents/guide/guide.css new file mode 100644 index 0000000000..e3d74bf009 --- /dev/null +++ b/dashboard-ui/bower_components/emby-webcomponents/guide/guide.css @@ -0,0 +1,398 @@ +.tvguide { + display: flex; + flex-direction: column; + align-items: initial; +} + +.tvGuideHeader { + white-space: nowrap; + width: 100%; + flex-shrink: 0; +} + +.tvProgramSectionHeader { + margin: 0; +} + +.tvProgram { + display: block; + text-decoration: none; + white-space: nowrap; + position: relative; +} + +.tvProgramTimeSlotInner { + padding: .5em; +} + +.tvProgramInfo { + vertical-align: middle; + padding: .5em .5em; + border-bottom: .65vh solid #121212; +} + +.tvProgramCurrentTimeSlot { + background-color: green; +} + +.tvProgramName { + color: #fff; + margin-bottom: .5em; +} + +.tvProgramTime { + color: #fff; +} + +.newTvProgram { + color: yellow; + text-transform: uppercase; +} + +.liveTvProgram { + color: #64A239; + text-transform: uppercase; +} + +.premiereTvProgram { + color: orange; + text-transform: uppercase; +} + +.programAccent { + position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 2px; +} + +.sportsProgramInfo .programAccent { + background-color: #0A7C33; +} + +.newsProgramInfo .programAccent { + background-color: #523378; +} + +.movieProgramInfo .programAccent { + background-color: #A43913; +} + +.childProgramInfo .programAccent { + background-color: #0B487D; +} + +.timerCircle { + display: inline-block; + width: 1em; + height: 1em; + border-radius: 50%; + background: #cc0000; + border: 1px solid #cc3333; + margin-left: 1em; +} + +.seriesTimerCircle { + position: relative; + margin-left: 0; + left: 21px; + opacity: .3; +} + +.itemMiscInfo .seriesTimerCircle:first-child { + margin-left: 1em; +} + +.seriesTimerCircle + .seriesTimerCircle { + opacity: .65; + left: 0; +} + + .seriesTimerCircle + .seriesTimerCircle + .seriesTimerCircle { + opacity: 1; + left: -21px; + } + +.channelTimeslotHeader { + float: left; +} + +.timeslotHeaders { + white-space: nowrap; +} + +.mobileGuide .timeslotHeaders { + overflow-x: hidden; +} + +.programContainer { + white-space: nowrap; + position: relative; +} + +.programGridContainer { + margin-left: 12.2vw; +} + +.channelPrograms { + white-space: nowrap; + position: relative; +} + +.channelPrograms, .timeslotHeadersInner { + width: 1800vw; +} + +@media all and (min-width: 600px) { + + .channelPrograms, .timeslotHeadersInner { + width: 1400vw; + } +} + +@media all and (min-width: 800px) { + + .channelPrograms, .timeslotHeadersInner { + width: 1200vw; + } +} + +@media all and (min-width: 1280px) { + + .channelPrograms, .timeslotHeadersInner { + width: 660vw; + } +} + +.timeslotHeader { + display: inline-flex; + align-items: center; + text-indent: .35em; +} + +.channelHeaderCell, .channelTimeslotHeader { + overflow: hidden; + text-overflow: ellipsis; + border-right: 1px solid #121212; + width: 24vw; + background: rgba(40, 40, 40, .9); + display: flex; + align-items: center; + color: #fff !important; + text-decoration: none; +} + +@media all and (min-width: 500px) { + + .channelHeaderCell, .channelTimeslotHeader { + width: 16vw; + } +} + +@media all and (min-width: 600px) { + + .channelHeaderCell, .channelTimeslotHeader { + width: 16vw; + } +} + +@media all and (min-width: 800px) { + + .channelHeaderCell, .channelTimeslotHeader { + width: 14vw; + } +} + +@media all and (min-width: 1280px) { + + .channelHeaderCell, .channelTimeslotHeader { + width: 12vw; + } +} + +.btnSelectDate { + color: #fff; + padding-left: .5em; +} + +.channelHeaderCell { + border-bottom: .65vh solid #121212 !important; + background-size: auto 65.7%; + background-position: 90% center; + background-repeat: no-repeat; +} + +@media all and (max-width: 800px) { + + .guideCurrentDay { + display: none; + } +} + +@media all and (max-width: 1280px) { + + .guideChannelInfoWithImage .guideChannelName { + display: none; + } +} + +.channelPrograms, .channelHeaderCell { + height: 15vh; +} + +@media all and (min-height: 500px) { + + .channelPrograms, .channelHeaderCell { + height: 10.5vh; + } +} + +@media all and (min-height: 600px) { + + .channelPrograms, .channelHeaderCell { + height: 9vh; + } +} + +@media all and (min-height: 720px) { + + .channelPrograms, .channelHeaderCell { + height: 6vh; + } + + .layout-tv .channelPrograms, .layout-tv .channelHeaderCell { + height: 7.6vh; + } +} + +.channelTimeslotHeader { + border-right-color: transparent; +} + +.channelTimeslotHeader, .timeslotHeader { + background: transparent; +} + +.timeslotHeader, .channelTimeslotHeader { + height: 14vh; +} + +@media all and (min-height: 500px) { + + .timeslotHeader, .channelTimeslotHeader { + height: 10vh; + } +} + +@media all and (min-height: 600px) { + + .timeslotHeader, .channelTimeslotHeader { + height: 8vh; + } +} + +@media all and (min-height: 720px) { + + .timeslotHeader, .channelTimeslotHeader { + height: 5.75vh; + } +} + +.pointerInput .channelHeaderCell:hover { + background-color: #444; +} + +.channelList { + float: left; +} + +.programGrid { + padding-bottom: 4px; +} + +.timeslotHeader { + width: 2.0833333333333333333333333333333%; +} + +.programCell { + position: absolute; + top: 0; + /* Unfortunately the borders using vh get rounded while the bottom property doesn't. So this is a little hack to try and make them even*/ + bottom: .59vh; + border-left: .65vh solid #121212 !important; + background-color: rgba(32, 32, 32, .95); + display: flex; + color: #fff !important; + text-decoration: none; + overflow: hidden; + align-items: center; +} + +.timeslotCellInner { + position: absolute; + bottom: 0; + overflow: hidden; + width: 100%; + top: 0; + display: block; + text-decoration: none; + color: #fff !important; +} + +.guideProgramName { + padding: 0 .5em 0; + overflow: hidden; + text-overflow: ellipsis; +} + +.guideProgramTime { + padding: 0 .5em .35em; + color: #bbb; +} + +.programCell iron-icon { + margin-left: auto; + margin-right: .5em; + height: 3.5vh; + width: 3.5vh; + color: #ddd; + flex-shrink: 0; +} + + .programCell iron-icon + iron-icon { + margin-left: .25em; + } + +.guideChannelInfo { + display: inline-block; + max-width: 110px; + overflow: hidden; + white-space: nowrap; + padding-left: .7em; +} + +.guideChannelName { + margin-left: auto; + margin-right: .3em; + max-width: 6vw; + text-overflow: ellipsis; + overflow: hidden; +} + +.channelList, .programGrid { + height: auto !important; +} + +.programCell:focus, .channelHeaderCell:focus, .btnSelectDate:focus { + background-color: #52B54B; + outline: none !important; +} + + .programCell:focus .programAccent { + background-color: transparent !important; + } + +.timerIcon, .seriesTimerIcon { + color: #cc3333 !important; +} diff --git a/dashboard-ui/bower_components/emby-webcomponents/guide/guide.js b/dashboard-ui/bower_components/emby-webcomponents/guide/guide.js new file mode 100644 index 0000000000..f608a62615 --- /dev/null +++ b/dashboard-ui/bower_components/emby-webcomponents/guide/guide.js @@ -0,0 +1,641 @@ +define(['globalize', 'connectionManager', 'loading', 'scrollHelper', 'datetime', 'focusManager', 'imageLoader', 'events', 'layoutManager', 'itemShortcuts', 'registrationservices', 'clearButtonStyle', 'css!./guide.css', 'html!./icons.html', 'scrollStyles'], function (globalize, connectionManager, loading, scrollHelper, datetime, focusManager, imageLoader, events, layoutManager, itemShortcuts, registrationServices) { + + var baseUrl; + + function Guide(options) { + + var self = this; + var items = {}; + + self.refresh = function () { + reloadPage(options.element); + }; + + self.destroy = function () { + itemShortcuts.off(options.element); + items = {}; + }; + + self.options = options; + + // 30 mins + var cellCurationMinutes = 30; + var cellDurationMs = cellCurationMinutes * 60 * 1000; + var msPerDay = 86400000; + + var currentDate; + + var channelQuery = { + + StartIndex: 0, + EnableFavoriteSorting: true + }; + + var channelsPromise; + + function normalizeDateToTimeslot(date) { + + var minutesOffset = date.getMinutes() - cellCurationMinutes; + + if (minutesOffset >= 0) { + + date.setHours(date.getHours(), cellCurationMinutes, 0, 0); + + } else { + + date.setHours(date.getHours(), 0, 0, 0); + } + + return date; + } + + function showLoading() { + loading.show(); + } + + function hideLoading() { + loading.hide(); + } + + function getChannelLimit(context) { + + return registrationServices.validateFeature('livetv').then(function () { + + var limit = 400; + + context.querySelector('.guideRequiresUnlock').classList.add('hide'); + + return limit; + + }, function () { + + var limit = 5; + + context.querySelector('.guideRequiresUnlock').classList.remove('hide'); + context.querySelector('.unlockText').innerHTML = globalize.translate('MessageLiveTvGuideRequiresUnlock', limit); + + return limit; + }); + } + + function reloadGuide(context, newStartDate) { + + var apiClient = connectionManager.currentApiClient(); + + channelQuery.UserId = apiClient.getCurrentUserId(); + + getChannelLimit(context).then(function (channelLimit) { + + showLoading(); + + channelQuery.Limit = channelLimit; + channelQuery.AddCurrentProgram = false; + + channelsPromise = channelsPromise || apiClient.getLiveTvChannels(channelQuery); + + var date = newStartDate; + // Add one second to avoid getting programs that are just ending + date = new Date(date.getTime() + 1000); + + // Subtract to avoid getting programs that are starting when the grid ends + var nextDay = new Date(date.getTime() + msPerDay - 2000); + + console.log(nextDay); + channelsPromise.then(function (channelsResult) { + + apiClient.getLiveTvPrograms({ + UserId: apiClient.getCurrentUserId(), + MaxStartDate: nextDay.toISOString(), + MinEndDate: date.toISOString(), + channelIds: channelsResult.Items.map(function (c) { + return c.Id; + }).join(','), + ImageTypeLimit: 1, + EnableImageTypes: "Primary,Backdrop", + SortBy: "StartDate" + + }).then(function (programsResult) { + + renderGuide(context, date, channelsResult.Items, programsResult.Items, apiClient); + + hideLoading(); + + }); + }); + }); + } + + function getDisplayTime(date) { + + if ((typeof date).toString().toLowerCase() === 'string') { + try { + + date = datetime.parseISO8601Date(date, { toLocal: true }); + + } catch (err) { + return date; + } + } + + return datetime.getDisplayTime(date).toLowerCase(); + } + + function getTimeslotHeadersHtml(startDate, endDateTime) { + + var html = ''; + + // clone + startDate = new Date(startDate.getTime()); + + html += '
'; + + while (startDate.getTime() < endDateTime) { + + html += '
'; + + html += getDisplayTime(startDate); + html += '
'; + + // Add 30 mins + startDate.setTime(startDate.getTime() + cellDurationMs); + } + html += '
'; + + return html; + } + + function parseDates(program) { + + if (!program.StartDateLocal) { + try { + + program.StartDateLocal = datetime.parseISO8601Date(program.StartDate, { toLocal: true }); + + } catch (err) { + + } + + } + + if (!program.EndDateLocal) { + try { + + program.EndDateLocal = datetime.parseISO8601Date(program.EndDate, { toLocal: true }); + + } catch (err) { + + } + + } + + return null; + } + + function getChannelProgramsHtml(context, date, channel, programs) { + + var html = ''; + + var startMs = date.getTime(); + var endMs = startMs + msPerDay - 1; + + programs = programs.filter(function (curr) { + return curr.ChannelId == channel.Id; + }); + + html += '
'; + + for (var i = 0, length = programs.length; i < length; i++) { + + var program = programs[i]; + + if (program.ChannelId != channel.Id) { + continue; + } + + parseDates(program); + + if (program.EndDateLocal.getTime() < startMs) { + continue; + } + + if (program.StartDateLocal.getTime() > endMs) { + break; + } + + items[program.Id] = program; + + var renderStartMs = Math.max(program.StartDateLocal.getTime(), startMs); + var startPercent = (program.StartDateLocal.getTime() - startMs) / msPerDay; + startPercent *= 100; + startPercent = Math.max(startPercent, 0); + + var renderEndMs = Math.min(program.EndDateLocal.getTime(), endMs); + var endPercent = (renderEndMs - renderStartMs) / msPerDay; + endPercent *= 100; + + var cssClass = "programCell clearButton itemAction"; + var addAccent = true; + + if (program.IsKids) { + cssClass += " childProgramInfo"; + } else if (program.IsSports) { + cssClass += " sportsProgramInfo"; + } else if (program.IsNews) { + cssClass += " newsProgramInfo"; + } else if (program.IsMovie) { + cssClass += " movieProgramInfo"; + } + else { + cssClass += " plainProgramInfo"; + addAccent = false; + } + + html += ''; + } + + html += '
'; + + return html; + } + + function renderPrograms(context, date, channels, programs) { + + var html = []; + + for (var i = 0, length = channels.length; i < length; i++) { + + html.push(getChannelProgramsHtml(context, date, channels[i], programs)); + } + + var programGrid = context.querySelector('.programGrid'); + programGrid.innerHTML = html.join(''); + + programGrid.scrollTop = 0; + programGrid.scrollLeft = 0; + } + + function renderChannelHeaders(context, channels, apiClient) { + + var html = ''; + + for (var i = 0, length = channels.length; i < length; i++) { + + var channel = channels[i]; + var hasChannelImage = channel.ImageTags.Primary; + var dataSrc = ''; + if (hasChannelImage) { + + var url = apiClient.getScaledImageUrl(channel.Id, { + maxHeight: 200, + tag: channel.ImageTags.Primary, + type: "Primary" + }); + + dataSrc = ' data-src="' + url + '"'; + } + + html += ''; + } + + var channelList = context.querySelector('.channelList'); + channelList.innerHTML = html; + imageLoader.lazyChildren(channelList); + } + + function renderGuide(context, date, channels, programs, apiClient) { + + //var list = []; + //channels.forEach(function(i) { + // list.push(i); + //}); + //channels.forEach(function (i) { + // list.push(i); + //}); + //channels.forEach(function (i) { + // list.push(i); + //}); + //channels.forEach(function (i) { + // list.push(i); + //}); + //channels.forEach(function (i) { + // list.push(i); + //}); + //channels.forEach(function (i) { + // list.push(i); + //}); + //channels.forEach(function (i) { + // list.push(i); + //}); + //channels.forEach(function (i) { + // list.push(i); + //}); + //channels.forEach(function (i) { + // list.push(i); + //}); + //channels.forEach(function (i) { + // list.push(i); + //}); + //channels.forEach(function (i) { + // list.push(i); + //}); + //channels.forEach(function (i) { + // list.push(i); + //}); + //channels = list; + renderChannelHeaders(context, channels, apiClient); + + var startDate = date; + var endDate = new Date(startDate.getTime() + msPerDay); + context.querySelector('.timeslotHeaders').innerHTML = getTimeslotHeadersHtml(startDate, endDate); + items = {}; + renderPrograms(context, date, channels, programs); + + if (layoutManager.tv) { + focusManager.autoFocus(context.querySelector('.programGrid'), true); + } + } + + function nativeScrollTo(container, pos, horizontal) { + + if (container.scrollTo) { + if (horizontal) { + container.scrollTo(pos, 0); + } else { + container.scrollTo(0, pos); + } + } else { + if (horizontal) { + container.scrollLeft = Math.round(pos); + } else { + container.scrollTop = Math.round(pos); + } + } + } + + var lastGridScroll = 0; + var lastHeaderScroll = 0; + function onProgramGridScroll(context, elem, timeslotHeaders) { + + if ((new Date().getTime() - lastHeaderScroll) >= 1000) { + lastGridScroll = new Date().getTime(); + nativeScrollTo(timeslotHeaders, elem.scrollLeft, true); + } + } + + function onTimeslotHeadersScroll(context, elem, programGrid) { + + if ((new Date().getTime() - lastGridScroll) >= 1000) { + lastHeaderScroll = new Date().getTime(); + nativeScrollTo(programGrid, elem.scrollLeft, true); + } + } + + function getFutureDateText(date) { + + var weekday = []; + weekday[0] = globalize.translate('core#OptionSundayShort'); + weekday[1] = globalize.translate('core#OptionMondayShort'); + weekday[2] = globalize.translate('core#OptionTuesdayShort'); + weekday[3] = globalize.translate('core#OptionWednesdayShort'); + weekday[4] = globalize.translate('core#OptionThursdayShort'); + weekday[5] = globalize.translate('core#OptionFridayShort'); + weekday[6] = globalize.translate('core#OptionSaturdayShort'); + + var day = weekday[date.getDay()]; + date = date.toLocaleDateString(); + + if (date.toLowerCase().indexOf(day.toLowerCase()) == -1) { + return day + " " + date; + } + + return date; + } + + function changeDate(page, date) { + + var newStartDate = normalizeDateToTimeslot(date); + currentDate = newStartDate; + + reloadGuide(page, newStartDate); + + var text = getFutureDateText(date); + text = '' + text.replace(' ', ' '); + page.querySelector('.btnSelectDate').innerHTML = text; + } + + var dateOptions = []; + + function setDateRange(page, guideInfo) { + + var today = new Date(); + today.setHours(today.getHours(), 0, 0, 0); + + var start = datetime.parseISO8601Date(guideInfo.StartDate, { toLocal: true }); + var end = datetime.parseISO8601Date(guideInfo.EndDate, { toLocal: true }); + + start.setHours(0, 0, 0, 0); + end.setHours(0, 0, 0, 0); + + if (start.getTime() >= end.getTime()) { + end.setDate(start.getDate() + 1); + } + + start = new Date(Math.max(today, start)); + + dateOptions = []; + + while (start <= end) { + + dateOptions.push({ + name: getFutureDateText(start), + id: start.getTime() + }); + + start.setDate(start.getDate() + 1); + start.setHours(0, 0, 0, 0); + } + + var date = new Date(); + + if (currentDate) { + date.setTime(currentDate.getTime()); + } + + changeDate(page, date); + } + + function reloadPage(page) { + + showLoading(); + + var apiClient = connectionManager.currentApiClient(); + + apiClient.getLiveTvGuideInfo().then(function (guideInfo) { + + setDateRange(page, guideInfo); + }); + } + + function selectDate(page) { + + require(['actionsheet'], function (actionsheet) { + + actionsheet.show({ + items: dateOptions, + title: globalize.translate('core#HeaderSelectDate'), + callback: function (id) { + + var date = new Date(); + date.setTime(parseInt(id)); + changeDate(page, date); + } + }); + + }); + } + + function createVerticalScroller(view, pageInstance) { + + if (layoutManager.tv) { + scrollHelper.centerFocus.on(view.querySelector('.smoothScrollY'), false); + + var programGrid = view.querySelector('.programGrid'); + + scrollHelper.centerFocus.on(programGrid, true); + } + } + + function parentWithClass(elem, className) { + + while (!elem.classList || !elem.classList.contains(className)) { + elem = elem.parentNode; + + if (!elem) { + return null; + } + } + + return elem; + } + + var selectedMediaInfoTimeout; + var focusedElement; + function onProgramGridFocus(e) { + + var programCell = parentWithClass(e.target, 'programCell'); + + if (!programCell) { + return; + } + + focusedElement = e.target; + if (selectedMediaInfoTimeout) { + clearTimeout(selectedMediaInfoTimeout); + } + selectedMediaInfoTimeout = setTimeout(onSelectedMediaInfoTimeout, 700); + } + + function onSelectedMediaInfoTimeout() { + var focused = focusedElement + if (focused && document.activeElement == focused) { + var id = focused.getAttribute('data-id'); + var item = items[id]; + + if (item) { + events.trigger(self, 'focus', [ + { + item: item + }]); + } + } + } + + var xhr = new XMLHttpRequest(); + xhr.open('GET', baseUrl + '/tvguide.template.html', true); + + xhr.onload = function (e) { + + var template = this.response; + var context = options.element; + context.innerHTML = globalize.translateDocument(template, 'core'); + + var programGrid = context.querySelector('.programGrid'); + var timeslotHeaders = context.querySelector('.timeslotHeaders'); + + programGrid.addEventListener('focus', onProgramGridFocus, true); + programGrid.addEventListener('scroll', function () { + + onProgramGridScroll(context, this, timeslotHeaders); + }); + + timeslotHeaders.addEventListener('scroll', function () { + onTimeslotHeadersScroll(context, this, programGrid); + }); + + context.querySelector('.btnSelectDate').addEventListener('click', function () { + selectDate(context); + }); + + context.querySelector('.btnUnlockGuide').addEventListener('click', function () { + reloadPage(context); + }); + + context.classList.add('tvguide'); + + createVerticalScroller(context, self); + itemShortcuts.on(context); + + events.trigger(self, 'load'); + + self.refresh(); + } + + xhr.send(); + }; + + Guide.setBaseUrl = function (url) { + baseUrl = url; + }; + + return Guide; +}); \ No newline at end of file diff --git a/dashboard-ui/bower_components/emby-webcomponents/guide/icons.html b/dashboard-ui/bower_components/emby-webcomponents/guide/icons.html new file mode 100644 index 0000000000..242d27d4eb --- /dev/null +++ b/dashboard-ui/bower_components/emby-webcomponents/guide/icons.html @@ -0,0 +1,42 @@ + + + + + + + + + + + + diff --git a/dashboard-ui/bower_components/emby-webcomponents/guide/tvguide.template.html b/dashboard-ui/bower_components/emby-webcomponents/guide/tvguide.template.html new file mode 100644 index 0000000000..523e8dd410 --- /dev/null +++ b/dashboard-ui/bower_components/emby-webcomponents/guide/tvguide.template.html @@ -0,0 +1,22 @@ +
+
+ +
+
+
+ +
+ +
+
+ +
+
+ +
+
+ +
+

+ ${ButtonUnlockGuide} +
\ No newline at end of file diff --git a/dashboard-ui/bower_components/emby-webcomponents/router.js b/dashboard-ui/bower_components/emby-webcomponents/router.js index cd95b32e0e..cda661f27a 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/router.js +++ b/dashboard-ui/bower_components/emby-webcomponents/router.js @@ -271,17 +271,17 @@ define(['loading', 'viewManager', 'skinManager', 'pluginManager', 'backdrop', 'b var apiClient = connectionManager.currentApiClient(); var pathname = ctx.pathname.toLowerCase(); - console.log('Emby.Page - processing path request ' + pathname); + console.log('embyRouter - processing path request ' + pathname); if ((!apiClient || !apiClient.isLoggedIn()) && !route.anonymous) { - console.log('Emby.Page - route does not allow anonymous access, redirecting to login'); + console.log('embyRouter - route does not allow anonymous access, redirecting to login'); beginConnectionWizard(); return; } if (apiClient && apiClient.isLoggedIn()) { - console.log('Emby.Page - user is authenticated'); + console.log('embyRouter - user is authenticated'); var isCurrentRouteStartup = currentRouteInfo ? currentRouteInfo.route.startup : true; if (ctx.isBack && (route.isDefaultRoute || route.startup) && !isCurrentRouteStartup) { @@ -289,7 +289,7 @@ define(['loading', 'viewManager', 'skinManager', 'pluginManager', 'backdrop', 'b return; } else if (route.isDefaultRoute) { - console.log('Emby.Page - loading skin home page'); + console.log('embyRouter - loading skin home page'); skinManager.loadUserSkin(); return; } else if (route.roles) { @@ -303,7 +303,7 @@ define(['loading', 'viewManager', 'skinManager', 'pluginManager', 'backdrop', 'b } } - console.log('Emby.Page - proceeding to ' + pathname); + console.log('embyRouter - proceeding to ' + pathname); callback(); } @@ -484,8 +484,10 @@ define(['loading', 'viewManager', 'skinManager', 'pluginManager', 'backdrop', 'b function showItem(item) { if (typeof (item) === 'string') { - Emby.Models.item(item).then(showItem); - + require(['connectionManager'], function (connectionManager) { + var apiClient = connectionManager.currentApiClient(); + apiClient.getItem(apiClient.getCurrentUserId(), item).then(showItem); + }); } else { skinManager.getCurrentSkin().showItem(item); } diff --git a/dashboard-ui/bower_components/emby-webcomponents/shortcuts.js b/dashboard-ui/bower_components/emby-webcomponents/shortcuts.js new file mode 100644 index 0000000000..05802cd3ab --- /dev/null +++ b/dashboard-ui/bower_components/emby-webcomponents/shortcuts.js @@ -0,0 +1,168 @@ +define(['playbackManager', 'inputManager', 'connectionManager', 'embyRouter'], function (playbackManager, inputManager, connectionManager, embyRouter) { + + function playAllFromHere(card, serverId) { + var cards = card.parentNode.querySelectorAll('.itemAction[data-id]'); + var ids = []; + + var foundCard = false; + for (var i = 0, length = cards.length; i < length; i++) { + if (cards[i] == card) { + foundCard = true; + } + if (foundCard) { + ids.push(cards[i].getAttribute('data-id')); + } + } + playbackManager.play({ + ids: ids, + serverId: serverId + }); + } + + function showSlideshow(startItemId, serverId) { + + var apiClient = connectionManager.getApiClient(serverId); + var userId = apiClient.getCurrentUserId(); + + return apiClient.getItem(userId, startItemId).then(function (item) { + + return apiClient.getItems(userId, { + + MediaTypes: 'Photo', + Filters: 'IsNotFolder', + ParentId: item.ParentId + + }).then(function (result) { + + var items = result.Items; + + var index = items.map(function (i) { + return i.Id; + + }).indexOf(startItemId); + + if (index == -1) { + index = 0; + } + + require(['slideshow'], function (slideshow) { + + var newSlideShow = new slideshow({ + showTitle: false, + cover: false, + items: items, + startIndex: index, + interval: 8000, + interactive: true + }); + + newSlideShow.show(); + }); + + }); + }); + } + + function showItem(options) { + + if (options.Type == 'Photo') { + + showSlideshow(options.Id, options.ServerId); + return; + } + + embyRouter.showItem(options); + } + + function executeAction(card, action) { + var id = card.getAttribute('data-id'); + var serverId = card.getAttribute('data-serverid'); + var type = card.getAttribute('data-type'); + var isfolder = card.getAttribute('data-isfolder') == 'true'; + + if (action == 'link') { + showItem({ + Id: id, + Type: type, + IsFolder: isfolder, + ServerId: serverId + }); + } + + else if (action == 'instantmix') { + playbackManager.instantMix(id, serverId); + } + + else if (action == 'play') { + + var startPositionTicks = parseInt(card.getAttribute('data-startpositionticks') || '0'); + + playbackManager.play({ + ids: [id], + startPositionTicks: startPositionTicks, + serverId: serverId + }); + } + + else if (action == 'playallfromhere') { + playAllFromHere(card, serverId); + } + + else if (action == 'setplaylistindex') { + + } + } + + function onClick(e) { + var card = parentWithClass(e.target, 'itemAction'); + + if (card) { + var action = card.getAttribute('data-action'); + + if (action) { + executeAction(card, action); + } + } + } + + function parentWithClass(elem, className) { + + while (!elem.classList || !elem.classList.contains(className)) { + elem = elem.parentNode; + + if (!elem) { + return null; + } + } + + return elem; + } + + function onCommand(e) { + var cmd = e.detail.command; + + if (cmd == 'play') { + var card = parentWithClass(e.target, 'itemAction'); + + if (card) { + executeAction(card, cmd); + } + } + } + + function on(context) { + context.addEventListener('click', onClick); + inputManager.on(context, onCommand); + } + + function off(context) { + context.removeEventListener('click', onClick); + inputManager.off(context, onCommand); + } + + return { + on: on, + off: off + }; + +}); \ No newline at end of file diff --git a/dashboard-ui/bower_components/iron-overlay-behavior/.bower.json b/dashboard-ui/bower_components/iron-overlay-behavior/.bower.json index 529d991c8c..35dc58b18a 100644 --- a/dashboard-ui/bower_components/iron-overlay-behavior/.bower.json +++ b/dashboard-ui/bower_components/iron-overlay-behavior/.bower.json @@ -1,6 +1,6 @@ { "name": "iron-overlay-behavior", - "version": "1.6.3", + "version": "1.6.4", "license": "http://polymer.github.io/LICENSE.txt", "description": "Provides a behavior for making an element an overlay", "private": true, @@ -35,11 +35,11 @@ }, "ignore": [], "homepage": "https://github.com/polymerelements/iron-overlay-behavior", - "_release": "1.6.3", + "_release": "1.6.4", "_resolution": { "type": "version", - "tag": "v1.6.3", - "commit": "5b331ebaefe3214937b94ba19769154efee46244" + "tag": "v1.6.4", + "commit": "983654132fd8281c3da07d79eea8a0f5b28e7a4f" }, "_source": "git://github.com/polymerelements/iron-overlay-behavior.git", "_target": "^1.0.0", diff --git a/dashboard-ui/bower_components/iron-overlay-behavior/bower.json b/dashboard-ui/bower_components/iron-overlay-behavior/bower.json index 05bdeb4e07..1be05487e4 100644 --- a/dashboard-ui/bower_components/iron-overlay-behavior/bower.json +++ b/dashboard-ui/bower_components/iron-overlay-behavior/bower.json @@ -1,6 +1,6 @@ { "name": "iron-overlay-behavior", - "version": "1.6.3", + "version": "1.6.4", "license": "http://polymer.github.io/LICENSE.txt", "description": "Provides a behavior for making an element an overlay", "private": true, diff --git a/dashboard-ui/bower_components/iron-overlay-behavior/iron-overlay-behavior.html b/dashboard-ui/bower_components/iron-overlay-behavior/iron-overlay-behavior.html index aca0171f66..68c7b3224a 100644 --- a/dashboard-ui/bower_components/iron-overlay-behavior/iron-overlay-behavior.html +++ b/dashboard-ui/bower_components/iron-overlay-behavior/iron-overlay-behavior.html @@ -113,7 +113,9 @@ context. You should place this element as a child of `` whenever possible. }, /** - * Returns the reason this dialog was last closed. + * Contains the reason(s) this overlay was last closed (see `iron-overlay-closed`). + * `IronOverlayBehavior` provides the `canceled` reason; implementers of the + * behavior can provide other reasons in addition to `canceled`. */ closingReason: { // was a getter before, but needs to be a property so other @@ -322,16 +324,23 @@ context. You should place this element as a child of `` whenever possible. this._manager.addOrRemoveOverlay(this); - this.__isAnimating = true; - - // requestAnimationFrame for non-blocking rendering if (this.__openChangedAsync) { window.cancelAnimationFrame(this.__openChangedAsync); } + + // Defer any animation-related code on attached + // (_openedChanged gets called again on attached). + if (!this.isAttached) { + return; + } + + this.__isAnimating = true; + if (this.opened) { if (this.withBackdrop) { this.backdropElement.prepare(); } + // requestAnimationFrame for non-blocking rendering this.__openChangedAsync = window.requestAnimationFrame(function() { this.__openChangedAsync = null; this._prepareRenderOpened(); @@ -574,24 +583,26 @@ context. You should place this element as a child of `` whenever possible. Polymer.IronOverlayBehavior = [Polymer.IronFitBehavior, Polymer.IronResizableBehavior, Polymer.IronOverlayBehaviorImpl]; /** - * Fired after the `iron-overlay` opens. - * @event iron-overlay-opened - */ + * Fired after the overlay opens. + * @event iron-overlay-opened + */ /** - * Fired when the `iron-overlay` is canceled, but before it is closed. - * Cancel the event to prevent the `iron-overlay` from closing. - * @event iron-overlay-canceled - * @param {Event} event The closing of the `iron-overlay` can be prevented - * by calling `event.preventDefault()`. The `event.detail` is the original event that originated - * the canceling (e.g. ESC keyboard event or click event outside the `iron-overlay`). - */ + * Fired when the overlay is canceled, but before it is closed. + * @event iron-overlay-canceled + * @param {Event} event The closing of the overlay can be prevented + * by calling `event.preventDefault()`. + * @param {Event} event.detail It is the original event that originated + * the canceling (e.g. ESC keyboard event or click event outside the overlay). + */ /** - * Fired after the `iron-overlay` closes. - * @event iron-overlay-closed - * @param {{canceled: (boolean|undefined)}} closingReason Contains `canceled` (whether the overlay was canceled). - */ + * Fired after the overlay closes. + * @event iron-overlay-closed + * @param {Event} event The event + * @param {Object} event.detail It is the `closingReason` property (contains + * `canceled`, whether the overlay was canceled). + */ })(); diff --git a/dashboard-ui/bower_components/iron-overlay-behavior/iron-overlay-manager.html b/dashboard-ui/bower_components/iron-overlay-behavior/iron-overlay-manager.html index 020855f4b5..e1452a9bc9 100644 --- a/dashboard-ui/bower_components/iron-overlay-behavior/iron-overlay-manager.html +++ b/dashboard-ui/bower_components/iron-overlay-behavior/iron-overlay-manager.html @@ -339,6 +339,12 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN var overlay = /** @type {?} */ (this.currentOverlay()); // Check if clicked outside of top overlay. if (overlay && this._overlayInPath(Polymer.dom(event).path) !== overlay) { + if (overlay.withBackdrop) { + // There's no need to stop the propagation as the backdrop element + // already got this mousedown/touchstart event. Calling preventDefault + // on this event ensures that click/tap won't be triggered at all. + event.preventDefault(); + } overlay._onCaptureClick(event); } }, diff --git a/dashboard-ui/bower_components/iron-overlay-behavior/test/iron-overlay-behavior.html b/dashboard-ui/bower_components/iron-overlay-behavior/test/iron-overlay-behavior.html index 4834a50eb6..0069d897b8 100644 --- a/dashboard-ui/bower_components/iron-overlay-behavior/test/iron-overlay-behavior.html +++ b/dashboard-ui/bower_components/iron-overlay-behavior/test/iron-overlay-behavior.html @@ -155,6 +155,21 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN assert.equal(getComputedStyle(overlay).display, 'none', 'overlay starts hidden'); }); + test('_renderOpened called only after is attached', function(done) { + var overlay = document.createElement('test-overlay'); + // The overlay is ready at this point, but not yet attached. + var spy = sinon.spy(overlay, '_renderOpened'); + // This triggers _openedChanged. + overlay.opened = true; + // Even if not attached yet, overlay should be the current overlay! + assert.equal(overlay, overlay._manager.currentOverlay(), 'currentOverlay ok'); + // Wait long enough for requestAnimationFrame callback. + overlay.async(function() { + assert.isFalse(spy.called, '_renderOpened not called'); + done(); + }, 100); + }); + test('overlay open/close events', function(done) { var nevents = 0; @@ -751,6 +766,27 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN done(); }); }); + + test('withBackdrop = true prevents click outside event', function(done) { + runAfterOpen(overlay, function() { + overlay.addEventListener('iron-overlay-canceled', function(event) { + assert.isTrue(event.detail.defaultPrevented, 'click event prevented'); + done(); + }); + MockInteractions.tap(document.body); + }); + }); + + test('withBackdrop = false does not prevent click outside event', function(done) { + overlay.withBackdrop = false; + runAfterOpen(overlay, function() { + overlay.addEventListener('iron-overlay-canceled', function(event) { + assert.isFalse(event.detail.defaultPrevented, 'click event not prevented'); + done(); + }); + MockInteractions.tap(document.body); + }); + }); }); suite('multiple overlays', function() { diff --git a/dashboard-ui/bower_components/iron-validatable-behavior/.bower.json b/dashboard-ui/bower_components/iron-validatable-behavior/.bower.json index 62842a49d8..6450b86c26 100644 --- a/dashboard-ui/bower_components/iron-validatable-behavior/.bower.json +++ b/dashboard-ui/bower_components/iron-validatable-behavior/.bower.json @@ -1,6 +1,6 @@ { "name": "iron-validatable-behavior", - "version": "1.0.5", + "version": "1.1.0", "description": "Provides a behavior for an element that validates user input", "authors": "The Polymer Authors", "keywords": [ @@ -9,9 +9,7 @@ "iron", "behavior" ], - "main": [ - "iron-validatable-behavior.html" - ], + "main": "iron-validatable-behavior.html", "private": true, "repository": { "type": "git", @@ -29,14 +27,14 @@ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0", "iron-validator-behavior": "PolymerElements/iron-validator-behavior#^1.0.0", "test-fixture": "PolymerElements/test-fixture#^1.0.0", - "web-component-tester": "*", + "web-component-tester": "^4.0.0", "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0" }, - "_release": "1.0.5", + "_release": "1.1.0", "_resolution": { "type": "version", - "tag": "v1.0.5", - "commit": "c1334b835892b3d7a329a8e6b8741d4be3a8d99c" + "tag": "v1.1.0", + "commit": "01ed585b28d8ab41367518f9aebd8442b9166bfe" }, "_source": "git://github.com/PolymerElements/iron-validatable-behavior.git", "_target": "^1.0.0", diff --git a/dashboard-ui/bower_components/iron-validatable-behavior/.github/ISSUE_TEMPLATE.md b/dashboard-ui/bower_components/iron-validatable-behavior/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000000..a69a2fdc20 --- /dev/null +++ b/dashboard-ui/bower_components/iron-validatable-behavior/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,33 @@ + +### Description + + +### Expected outcome + + + +### Actual outcome + + + +### Live Demo + + +### Steps to reproduce + + + +### Browsers Affected + +- [ ] Chrome +- [ ] Firefox +- [ ] Safari 9 +- [ ] Safari 8 +- [ ] Safari 7 +- [ ] Edge +- [ ] IE 11 +- [ ] IE 10 diff --git a/dashboard-ui/bower_components/iron-validatable-behavior/.travis.yml b/dashboard-ui/bower_components/iron-validatable-behavior/.travis.yml new file mode 100644 index 0000000000..e4dd52cd4d --- /dev/null +++ b/dashboard-ui/bower_components/iron-validatable-behavior/.travis.yml @@ -0,0 +1,23 @@ +language: node_js +sudo: required +before_script: + - npm install -g bower polylint web-component-tester + - bower install + - polylint +env: + global: + - secure: AsXK1s4viLG4eumYMxE0bJasVbWrpQ7JaUboMoK4Nzhmunx01udikRRAJifl3nkcc4m2GpzCb4Kmtyh5hxyLA05uE416VzmfDq3dGJKsNAQXbSescSVik63llq4HXAdOvek2eSo5wOVGNHslscIed5K2bJajXfPMIODJyBxo8aE= + - secure: V/H/0+/R8owujbwe3IDbND8v5zs4pWTe0hRCLefjgR9ci2lmNNP54Zb97nOZnLQqynafDSyrngL3+RP5F0fk1dC++cpuxQNKoYPwDFPZ41LAxp5dgtyxkb+J4eWtq0UilWF5thVt4oB1OWwPGtxMmSl+imXXKtq4a4Ov++t2Apo= +node_js: stable +addons: + firefox: latest + apt: + sources: + - google-chrome + packages: + - google-chrome-stable + sauce_connect: true +script: + - xvfb-run wct + - "if [ \"${TRAVIS_PULL_REQUEST}\" = \"false\" ]; then wct -s 'default'; fi" +dist: trusty diff --git a/dashboard-ui/bower_components/iron-validatable-behavior/CONTRIBUTING.md b/dashboard-ui/bower_components/iron-validatable-behavior/CONTRIBUTING.md new file mode 100644 index 0000000000..f147978a3e --- /dev/null +++ b/dashboard-ui/bower_components/iron-validatable-behavior/CONTRIBUTING.md @@ -0,0 +1,77 @@ + + +# Polymer Elements +## Guide for Contributors + +Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines: + +### Filing Issues + +**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions: + + 1. **Who will use the feature?** _“As someone filling out a form…”_ + 2. **When will they use the feature?** _“When I enter an invalid value…”_ + 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_ + +**If you are filing an issue to report a bug**, please provide: + + 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug: + + ```markdown + The `paper-foo` element causes the page to turn pink when clicked. + + ## Expected outcome + + The page stays the same color. + + ## Actual outcome + + The page turns pink. + + ## Steps to reproduce + + 1. Put a `paper-foo` element in the page. + 2. Open the page in a web browser. + 3. Click the `paper-foo` element. + ``` + + 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output). + + 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers. + +### Submitting Pull Requests + +**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request. + +When submitting pull requests, please provide: + + 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax: + + ```markdown + (For a single issue) + Fixes #20 + + (For multiple issues) + Fixes #32, fixes #40 + ``` + + 2. **A succinct description of the design** used to fix any related issues. For example: + + ```markdown + This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked. + ``` + + 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered. + +If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that! diff --git a/dashboard-ui/bower_components/iron-validatable-behavior/bower.json b/dashboard-ui/bower_components/iron-validatable-behavior/bower.json index f57fff7d56..f71147bcfe 100644 --- a/dashboard-ui/bower_components/iron-validatable-behavior/bower.json +++ b/dashboard-ui/bower_components/iron-validatable-behavior/bower.json @@ -1,6 +1,6 @@ { "name": "iron-validatable-behavior", - "version": "1.0.5", + "version": "1.1.0", "description": "Provides a behavior for an element that validates user input", "authors": "The Polymer Authors", "keywords": [ @@ -9,9 +9,7 @@ "iron", "behavior" ], - "main": [ - "iron-validatable-behavior.html" - ], + "main": "iron-validatable-behavior.html", "private": true, "repository": { "type": "git", @@ -29,7 +27,7 @@ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0", "iron-validator-behavior": "PolymerElements/iron-validator-behavior#^1.0.0", "test-fixture": "PolymerElements/test-fixture#^1.0.0", - "web-component-tester": "*", + "web-component-tester": "^4.0.0", "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0" } } diff --git a/dashboard-ui/bower_components/iron-validatable-behavior/iron-validatable-behavior.html b/dashboard-ui/bower_components/iron-validatable-behavior/iron-validatable-behavior.html index 8060713913..1b9beba953 100644 --- a/dashboard-ui/bower_components/iron-validatable-behavior/iron-validatable-behavior.html +++ b/dashboard-ui/bower_components/iron-validatable-behavior/iron-validatable-behavior.html @@ -12,6 +12,10 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN diff --git a/dashboard-ui/bower_components/iron-validatable-behavior/test/dogs-only.html b/dashboard-ui/bower_components/iron-validatable-behavior/test/dogs-only.html new file mode 100644 index 0000000000..1b462a4186 --- /dev/null +++ b/dashboard-ui/bower_components/iron-validatable-behavior/test/dogs-only.html @@ -0,0 +1,30 @@ + + + + + + diff --git a/dashboard-ui/bower_components/iron-validatable-behavior/test/index.html b/dashboard-ui/bower_components/iron-validatable-behavior/test/index.html index 05194fdd8f..605c95ad77 100644 --- a/dashboard-ui/bower_components/iron-validatable-behavior/test/index.html +++ b/dashboard-ui/bower_components/iron-validatable-behavior/test/index.html @@ -1,5 +1,4 @@ - - - - +--> - paper-validatable-behavior tests + iron-validatable-behavior tests @@ -23,13 +20,13 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN - - + + + diff --git a/dashboard-ui/bower_components/iron-validatable-behavior/test/iron-validatable-behavior.html b/dashboard-ui/bower_components/iron-validatable-behavior/test/iron-validatable-behavior.html index 847a3f8f09..a8040ef479 100644 --- a/dashboard-ui/bower_components/iron-validatable-behavior/test/iron-validatable-behavior.html +++ b/dashboard-ui/bower_components/iron-validatable-behavior/test/iron-validatable-behavior.html @@ -1,5 +1,6 @@