diff --git a/dashboard-ui/css/images/items/searchhints/film.png b/dashboard-ui/css/images/items/searchhints/film.png new file mode 100644 index 0000000000..9c343a8fa2 Binary files /dev/null and b/dashboard-ui/css/images/items/searchhints/film.png differ diff --git a/dashboard-ui/css/images/items/searchhints/game.png b/dashboard-ui/css/images/items/searchhints/game.png new file mode 100644 index 0000000000..9fa39e569a Binary files /dev/null and b/dashboard-ui/css/images/items/searchhints/game.png differ diff --git a/dashboard-ui/css/images/items/searchhints/music.png b/dashboard-ui/css/images/items/searchhints/music.png new file mode 100644 index 0000000000..2d666ab7da Binary files /dev/null and b/dashboard-ui/css/images/items/searchhints/music.png differ diff --git a/dashboard-ui/css/images/items/searchhints/person.png b/dashboard-ui/css/images/items/searchhints/person.png new file mode 100644 index 0000000000..f5660b7a71 Binary files /dev/null and b/dashboard-ui/css/images/items/searchhints/person.png differ diff --git a/dashboard-ui/css/images/items/searchhints/tv.png b/dashboard-ui/css/images/items/searchhints/tv.png new file mode 100644 index 0000000000..2e43d98368 Binary files /dev/null and b/dashboard-ui/css/images/items/searchhints/tv.png differ diff --git a/dashboard-ui/css/search.css b/dashboard-ui/css/search.css index 6728acc4c5..e1a49b3bfd 100644 --- a/dashboard-ui/css/search.css +++ b/dashboard-ui/css/search.css @@ -1,6 +1,7 @@ .headerSearch { display: none; margin-right: 3em; + position: relative; } .txtSearch { @@ -11,12 +12,6 @@ width: 150px; } -.searchHints { - width: 148px; - color: #000; - font-family: Arial; -} - .btnSearch { vertical-align: middle; background-color: #4d90fe; @@ -50,16 +45,62 @@ } .searchHints { + width: 148px; + color: #000; background-color: #fff; - border: 1px solid #ccc; + border: 1px solid #999; position: absolute; z-index: 999; + margin-top: 1px; + font-size: 14px; } .searchHintsContent { padding: 10px; } +.searchHint { + display: block; + text-decoration: none; + color: #000; + border-bottom: 1px solid #eee; +} + + .searchHint:hover { + background-color: #eee; + } + +.searchHintImage { + display: inline-block; + width: 15%; + vertical-align: middle; +} + +.searchHintContent { + vertical-align: top; + display: inline-block; + width: 85%; +} + +.searchHintName { + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; +} + +.searchHintSecondaryText { + font-size: 11px; + color: #999; + margin-top: 3px; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; +} + +.searchHintContentInner { + padding: 2px 5px; +} + @media all and (min-width: 650px) { .headerSearch { @@ -87,3 +128,31 @@ width: 248px; } } + +@media all and (min-width: 1200px) { + .txtSearch { + width: 350px; + } + + .searchHints { + width: 348px; + } +} + +@media all and (min-width: 1440px) { + .txtSearch { + width: 450px; + } + + .searchHints { + width: 448px; + } + + .searchHintImage { + width: 12%; + } + + .searchHintContent { + width: 88%; + } +} diff --git a/dashboard-ui/scripts/librarybrowser.js b/dashboard-ui/scripts/librarybrowser.js index 087e8ace69..f5f45a7d66 100644 --- a/dashboard-ui/scripts/librarybrowser.js +++ b/dashboard-ui/scripts/librarybrowser.js @@ -282,17 +282,22 @@ return item.url; } + itemByNameContext = itemByNameContext || ""; + + // Handle search hints + var id = item.Id || item.ItemId; + if (item.Type == "Series") { - return "itemdetails.html?id=" + item.Id; + return "itemdetails.html?id=" + id; } if (item.Type == "Season") { - return "itemdetails.html?id=" + item.Id; + return "itemdetails.html?id=" + id; } if (item.Type == "BoxSet") { - return "itemdetails.html?id=" + item.Id; + return "itemdetails.html?id=" + id; } if (item.Type == "MusicAlbum") { - return "itemdetails.html?id=" + item.Id; + return "itemdetails.html?id=" + id; } if (item.Type == "Genre") { return "itembynamedetails.html?genre=" + encodeName(item.Name) + "&context=" + itemByNameContext; @@ -304,10 +309,10 @@ return "itembynamedetails.html?person=" + encodeName(item.Name) + "&context=" + itemByNameContext; } if (item.Type == "Artist") { - return "itembynamedetails.html?artist=" + encodeName(item.Name) + "&context=" + itemByNameContext; + return "itembynamedetails.html?artist=" + encodeName(item.Name) + "&context=" + (itemByNameContext || "music"); } - return item.IsFolder ? (item.Id ? "itemList.html?parentId=" + item.Id : "#") : "itemdetails.html?id=" + item.Id; + return item.IsFolder ? (id ? "itemList.html?parentId=" + id : "#") : "itemdetails.html?id=" + id; }, @@ -340,7 +345,8 @@ return ApiClient.getArtistImageUrl(item.Name, options); } - return ApiClient.getImageUrl(item.Id, options); + // For search hints + return ApiClient.getImageUrl(item.Id || item.ItemId, options); }, diff --git a/dashboard-ui/scripts/search.js b/dashboard-ui/scripts/search.js index 4466db0bf9..28efdeb2bd 100644 --- a/dashboard-ui/scripts/search.js +++ b/dashboard-ui/scripts/search.js @@ -1,41 +1,205 @@ -(function ($, document, window) { +(function ($, document, window, clearTimeout, setTimeout) { + var searchHintTimeout; - function createSearchHintsElement() { + function clearSearchHintTimeout() { - $(document.body).append('
Coming soon
').off("mousedown.hidesearchhints").on("mousedown.hidesearchhints", function (e) { + if (searchHintTimeout) { + + clearTimeout(searchHintTimeout); + searchHintTimeout = null; + } + } + + function createSearchHintsElement(page) { + + $(document.body).off("mousedown.hidesearchhints").on("mousedown.hidesearchhints", function (e) { var elem = $(e.target); if (!elem.is('#searchHints,#txtSearch,#btnSearch') && !elem.parents('#searchHints,#txtSearch,#btnSearch').length) { - - $('#searchHints').remove(); - - $(document.body).off("mousedown.hidesearchhints"); + hideFlyout(page); } }); - var txtElem = $('#txtSearch'); - var pos = txtElem.offset(); - - var hints = $('#searchHints')[0]; - - hints.style.top = txtElem[0].offsetHeight + pos.top + 1 + "px"; - hints.style.left = pos.left + "px"; - + return $('#searchHints', page).show(); } - function renderSearchHints(searchTerm) { + function hideFlyout(page) { + $('#searchHints', page).hide(); - var hints = $('#searchHints'); + $(document.body).off("mousedown.hidesearchhints"); + } + + function showFlyout(page) { + + var hints = $('#searchHints:visible'); if (!hints.length) { - hints = createSearchHintsElement(); + createSearchHintsElement(page); } } + function updateFlyout(page, searchTerm) { + + if (!searchTerm) { + + clearSearchHintTimeout(); + hideFlyout(page); + return; + } + + clearSearchHintTimeout(); + + searchHintTimeout = setTimeout(function () { + + requestSearchHints(page, searchTerm); + + }, 100); + } + + function requestSearchHints(page, searchTerm) { + + var currentTimeout = searchHintTimeout; + + $.getJSON(ApiClient.getUrl("Search/Hints", { userId: Dashboard.getCurrentUserId(), searchTerm: searchTerm, limit: 10 })).done(function (result) { + + if (currentTimeout != searchHintTimeout) { + return; + } + + if (!result.TotalRecordCount) { + hideFlyout(page); + return; + } + + renderSearchHintResult(page, result.SearchHints); + }); + } + + function preg_quote(str, delimiter) { + // http://kevin.vanzonneveld.net + // + original by: booeyOH + // + improved by: Ates Goral (http://magnetiq.com) + // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + bugfixed by: Onno Marsman + // + improved by: Brett Zamir (http://brett-zamir.me) + // * example 1: preg_quote("$40"); + // * returns 1: '\$40' + // * example 2: preg_quote("*RRRING* Hello?"); + // * returns 2: '\*RRRING\* Hello\?' + // * example 3: preg_quote("\\.+*?[^]$(){}=!<>|:"); + // * returns 3: '\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:' + return (str + '').replace(new RegExp('[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\' + (delimiter || '') + '-]', 'g'), '\\$&'); + } + + function getHintDisplayName(data, term) { + + var regexp = new RegExp("(" + preg_quote(term) + ")", 'gi'); + + return data.replace(regexp, "$1"); + } + + function getSearchHintHtml(hint) { + + var html = ''; + + html += ''; + + var imgUrl; + + if (hint.PrimaryImageTag) { + + hint.ImageTags = { Primary: hint.PrimaryImageTag }; + imgUrl = LibraryBrowser.getImageUrl(hint, "Primary", 0, { maxwidth: 150, maxheight: 150 }); + + } + else if (hint.MediaType == "Game") { + + imgUrl = "css/images/items/searchhints/game.png"; + + } + else if (hint.Type == "Episode" || hint.Type == "Season" || hint.Type == "Series") { + + imgUrl = "css/images/items/searchhints/tv.png"; + + } + else if (hint.Type == "Audio" || hint.Type == "MusicAlbum" || hint.Type == "Artist") { + + imgUrl = "css/images/items/searchhints/music.png"; + + } + else if (hint.Type == "Person") { + + imgUrl = "css/images/items/searchhints/person.png"; + + } + else { + + imgUrl = "css/images/items/searchhints/film.png"; + } + + html += ''; + + html += '
'; + + html += '
' + getHintDisplayName(hint.Name, hint.MatchedTerm) + '
'; + + if (hint.Type == "Audio") { + + html += '
' + [hint.AlbumArtist, hint.Album].join(" - ") + '
'; + + } + else if (hint.Type == "MusicAlbum") { + + html += '
' + hint.AlbumArtist + '
'; + + } + else { + html += '
' + (hint.DisplayMediaType || hint.Type) + '
'; + } + + var text; + + if (hint.EpisodeCount) { + + text = hint.EpisodeCount == 1 ? "1 Episode" : hint.EpisodeCount + " Episodes"; + + html += '
' + text + '
'; + } + + else if (hint.SongCount) { + + text = hint.SongCount == 1 ? "1 Song" : hint.SongCount + " Songs"; + + html += '
' + text + '
'; + } + + else if (hint.RunTimeTicks) { + html += '
' + DashboardPage.getDisplayText(hint.RunTimeTicks) + '
'; + } + + html += '
'; + + + html += '
'; + + return html; + } + + function renderSearchHintResult(page, hints) { + + var html = ''; + + for (var i = 0, length = hints.length; i < length; i++) { + html += getSearchHintHtml(hints[i]); + } + + $('#searchHints', page).html(html); + } + function search() { var self = this; @@ -50,14 +214,17 @@ html += ''; html += ''; - html += '
'; + html += ''; + + html += ''; + + html += '
'; return html; }; self.onSearchRendered = function (parentElem) { - $('#searchForm', parentElem).on("submit", function () { Dashboard.alert('Coming soon.'); @@ -65,16 +232,19 @@ return false; }); - $('#txtSearch', parentElem).on("keypress", function () { + $('#txtSearch', parentElem).on("keyup", function (e) { - renderSearchHints(this.value); + var value = this.value; + + showFlyout(parentElem); + updateFlyout(parentElem, value); }).on("focus", function () { var value = this.value; - + if (value) { - renderSearchHints(value); + showFlyout(parentElem, value); } }); @@ -84,4 +254,4 @@ window.Search = new search(); -})(jQuery, document, window); \ No newline at end of file +})(jQuery, document, window, clearTimeout, setTimeout); \ No newline at end of file diff --git a/dashboard-ui/scripts/site.js b/dashboard-ui/scripts/site.js index a3614027bc..15a619fa58 100644 --- a/dashboard-ui/scripts/site.js +++ b/dashboard-ui/scripts/site.js @@ -696,7 +696,9 @@ var Dashboard = { if (user && !page.hasClass('wizardPage')) { - headerHtml += Search.getSearchHtml(); + if (isLibraryPage) { + headerHtml += Search.getSearchHtml(); + } headerHtml += '' + user.Name + '';