diff options
Diffstat (limited to 'debian/webinterface-default/js')
-rwxr-xr-x | debian/webinterface-default/js/MediaLibrary.js | 1420 | ||||
-rwxr-xr-x | debian/webinterface-default/js/NowPlayingManager.js | 661 | ||||
-rw-r--r-- | debian/webinterface-default/js/json2.js | 492 | ||||
-rw-r--r-- | debian/webinterface-default/js/xbmc.core.js | 90 | ||||
-rw-r--r-- | debian/webinterface-default/js/xbmc.init.js | 27 | ||||
-rw-r--r-- | debian/webinterface-default/js/xbmc.launcher.js | 46 | ||||
-rw-r--r-- | debian/webinterface-default/js/xbmc.rpc.js | 49 |
7 files changed, 2785 insertions, 0 deletions
diff --git a/debian/webinterface-default/js/MediaLibrary.js b/debian/webinterface-default/js/MediaLibrary.js new file mode 100755 index 0000000..3c1a1bc --- /dev/null +++ b/debian/webinterface-default/js/MediaLibrary.js @@ -0,0 +1,1420 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/>. + * + */ + +var MediaLibrary = function () { + this.init(); +}; +MediaLibrary.prototype = { + playlists: {}, + + init: function () { + this.bindControls(); + this.getPlaylists(); + }, + bindControls: function () { + $('#musicLibrary').click(jQuery.proxy(this.musicLibraryOpen, this)); + $('#movieLibrary').click(jQuery.proxy(this.movieLibraryOpen, this)); + $('#tvshowLibrary').click(jQuery.proxy(this.tvshowLibraryOpen, this)); + $('#pictureLibrary').click(jQuery.proxy(this.pictureLibraryOpen, this)); + $('#remoteControl').click(jQuery.proxy(this.remoteControlOpen, this)); + $('#profiles').click(jQuery.proxy(this.profilesOpen, this)); + $('#overlay').click(jQuery.proxy(this.hideOverlay, this)); + $(window).resize(jQuery.proxy(this.updatePlayButtonLocation, this)); + $(document).on('keydown', jQuery.proxy(this.handleKeyPress, this)); + $(document).on('contextmenu', jQuery.proxy(this.handleContextMenu, this)); + }, + resetPage: function () { + $('#musicLibrary').removeClass('selected'); + $('#movieLibrary').removeClass('selected'); + $('#tvshowLibrary').removeClass('selected'); + $('#remoteControl').removeClass('selected'); + $('#pictureLibrary').removeClass('selected'); + $('#profiles').removeClass('selected'); + this.hideOverlay(); + }, + replaceAll: function (haystack, needle, thread) { + return (haystack || '').split(needle || '').join(thread || ''); + }, + getPlaylists: function () { + xbmc.rpc.request({ + 'context': this, + 'method': 'Playlist.GetPlaylists', + 'timeout': 3000, + 'success': function (data) { + if (data && data.result && data.result.length > 0) { + $.each($(data.result), jQuery.proxy(function (i, item) { + this.playlists[item.type] = item.playlistid; + }, this)); + } + }, + 'error': function (data, error) { + xbmc.core.displayCommunicationError(); + setTimeout(jQuery.proxy(this.updateState, this), 2000); + } + }); + }, + remoteControlOpen: function (event) { + this.resetPage(); + this.textBuffer = ''; + $('#remoteControl').addClass('selected'); + $('.contentContainer').hide(); + var libraryContainer = $('#remoteContainer'); + if (!libraryContainer || libraryContainer.length === 0) { + $('#spinner').show(); + libraryContainer = $('<div>'); + libraryContainer.attr('id', 'remoteContainer') + .addClass('contentContainer'); + $('#content').append(libraryContainer); + var keys = [{ + name: 'up', + width: '40px', + height: '30px', + top: '28px', + left: '58px' + }, + { + name: 'down', + width: '40px', + height: '30px', + top: '122px', + left: '58px' + }, + { + name: 'left', + width: '40px', + height: '30px', + top: '74px', + left: '15px' + }, + { + name: 'right', + width: '40px', + height: '30px', + top: '74px', + left: '104px' + }, + { + name: 'ok', + width: '40px', + height: '30px', + top: '74px', + left: '58px' + }, + { + name: 'back', + width: '40px', + height: '30px', + top: '13px', + left: '161px' + }, + { + name: 'home', + width: '40px', + height: '30px', + top: '154px', + left: '8px' + }, + { + name: 'mute', + width: '40px', + height: '30px', + top: '107px', + left: '391px' + }, + { + name: 'power', + width: '30px', + height: '30px', + top: '-3px', + left: '13px' + }, + { + name: 'volumeup', + width: '30px', + height: '30px', + top: '49px', + left: '422px' + }, + { + name: 'volumedown', + width: '30px', + height: '30px', + top: '49px', + left: '367px' + }, + { + name: 'playpause', + width: '32px', + height: '23px', + top: '62px', + left: '260px' + }, + { + name: 'stop', + width: '32px', + height: '23px', + top: '62px', + left: '211px' + }, + { + name: 'next', + width: '38px', + height: '25px', + top: '102px', + left: '304px' + }, + { + name: 'previous', + width: '38px', + height: '25px', + top: '101px', + left: '160px' + }, + { + name: 'forward', + width: '32px', + height: '23px', + top: '102px', + left: '259px' + }, + { + name: 'rewind', + width: '32px', + height: '23px', + top: '101px', + left: '211px' + }, + { + name: 'cleanlib_a', + width: '46px', + height: '26px', + top: '47px', + left: '553px' + }, + { + name: 'updatelib_a', + width: '46px', + height: '26px', + top: '47px', + left: '492px' + }, + { + name: 'cleanlib_v', + width: '46px', + height: '26px', + top: '111px', + left: '553px' + }, + { + name: 'updatelib_v', + width: '46px', + height: '26px', + top: '111px', + left: '492px' + } + ]; + for (var akey in keys) { + var aremotekey = $('<p>').attr('id', keys[akey]['name']); + aremotekey.addClass('remote_key') + .css('height', keys[akey]['height']) + .css('width', keys[akey]['width']) + .css('top', keys[akey]['top']) + .css('left', keys[akey]['left']) + .bind('click', { + key: keys[akey]['name'] + }, jQuery.proxy(this.pressRemoteKey, this)); + libraryContainer.append(aremotekey); + } + } else { + libraryContainer.show(); + libraryContainer.trigger('scroll'); + } + + $('#spinner').hide(); + }, + shouldHandleEvent: function (event) { + var inRemoteControl = $('#remoteControl').hasClass('selected'); + return (!event.ctrlKey && !event.altKey && inRemoteControl); + }, + handleKeyPress: function (event) { + if (!this.shouldHandleEvent(event)) { + return true; + } + + var keys = { + 8: 'back', // Back space + 13: 'ok', // Enter + 27: 'home', // Escape + 32: 'playpause', // Space bar + 37: 'left', // Left + 38: 'up', // Up + 39: 'right', // Right + 40: 'down', // Down + 93: 'contextmenu', // "Right Click" + 107: 'volumeup', // + (num keypad) + 109: 'volumedown', // - (num keypad) + 187: 'volumeup', // + (alnum keypad) + 189: 'volumedown' // - (alnum keypad) + }; + var which = event.which; + var key = keys[which]; + + event.data = { + key: key + }; + + if (!key) { + event.data.key = 'text'; + + if (event.key && event.key.length === 1) { + event.data.text = event.key; + } else { + // Letters + if (which >= 65 && which <= 90) { + var offset = event.shiftKey ? 0 : 32; + event.data.text = String.fromCharCode(which + offset); + } + + // Digits + if (which >= 96 && which <= 105) { + event.data.text = (which - 96) + ""; + } + } + } + + if (event.data.key) { + this.pressRemoteKey(event); + return false; + } + }, + handleContextMenu: function (event) { + if (!this.shouldHandleEvent(event)) { + return true; + } + if ( + (event.target == document) || //Chrome/Opera + (event.clientX === event.clientY && event.clientX === 0) //FF/IE + ) { + return false; + } //keyboard event. cancel it. + return true; + }, + rpcCall: function (method, params) { + var callObj = { + 'method': method + }; + if (params) { + callObj.params = params; + } + return xbmc.rpc.request(callObj); + }, + typeRemoteText: function (event) { + if (event.data.key === 'text' || ((event.data.key === 'playpause' || event.data.key === 'back') && this + .textBuffer.length)) { + if (event.data.key === 'back') { + this.textBuffer = this.textBuffer.substring(0, this.textBuffer.length - 1); + } else if (event.data.key === 'playpause') { + this.textBuffer += ' '; + } else if (event.data.text && event.data.text.length) { + this.textBuffer += event.data.text; + } + console.log(this.textBuffer); + return this.rpcCall('Input.SendText', { + 'text': this.textBuffer, + 'done': false + }); + } else { + this.textBuffer = ''; + } + }, + pressRemoteKey: function (event) { + var player = -1, + keyPressed = event.data.key; + $('#spinner').show(); + if (this.typeRemoteText(event)) { + return true; + } + + switch (keyPressed) { + case 'up': + return this.rpcCall('Input.Up'); + case 'down': + return this.rpcCall('Input.Down'); + case 'left': + return this.rpcCall('Input.Left'); + case 'right': + return this.rpcCall('Input.Right'); + case 'ok': + return this.rpcCall('Input.Select'); + case 'cleanlib_a': + return this.rpcCall('AudioLibrary.Clean'); + case 'updatelib_a': + return this.rpcCall('AudioLibrary.Scan'); + case 'cleanlib_v': + return this.rpcCall('VideoLibrary.Clean'); + case 'updatelib_v': + return this.rpcCall('VideoLibrary.Scan'); + case 'back': + return this.rpcCall('Input.Back'); + case 'home': + return this.rpcCall('Input.Home'); + case 'power': + return this.rpcCall('System.Shutdown'); + case 'contextmenu': + return this.rpcCall('Input.ContextMenu'); + case 'mute': + return this.rpcCall('Application.SetMute', { + 'mute': 'toggle' + }); + case 'volumeup': + return this.rpcCall('Application.SetVolume', { + 'volume': 'increment' + }); + case 'volumedown': + return this.rpcCall('Application.SetVolume', { + 'volume': 'decrement' + }); + } + + // TODO: Get active player + if ($('#videoDescription').is(':visible')) { + player = this.playlists["video"]; + } else if ($('#audioDescription').is(':visible')) { + player = this.playlists["audio"]; + } + + if (player >= 0) { + switch (keyPressed) { + case 'playpause': + return this.rpcCall('Player.PlayPause', { + 'playerid': player + }); + case 'stop': + return this.rpcCall('Player.Stop', { + 'playerid': player + }); + case 'next': + return this.rpcCall('Player.GoTo', { + 'playerid': player, + 'to': 'next' + }); + case 'previous': + return this.rpcCall('Player.GoTo', { + 'playerid': player, + 'to': 'previous' + }); + case 'forward': + return this.rpcCall('Player.SetSpeed', { + 'playerid': player, + 'speed': 'increment' + }); + case 'rewind': + return this.rpcCall('Player.SetSpeed', { + 'playerid': player, + 'speed': 'decrement' + }); + } + } + }, + musicLibraryOpen: function (event) { + this.resetPage(); + $('#musicLibrary').addClass('selected'); + $('.contentContainer').hide(); + var libraryContainer = $('#libraryContainer'); + if (!libraryContainer || libraryContainer.length === 0) { + $('#spinner').show(); + libraryContainer = $('<div>'); + libraryContainer.attr('id', 'libraryContainer') + .addClass('contentContainer'); + $('#content').append(libraryContainer); + xbmc.rpc.request({ + 'context': this, + 'method': 'AudioLibrary.GetAlbums', + 'params': { + 'limits': { + 'start': 0 + }, + 'properties': [ + 'description', + 'theme', + 'mood', + 'style', + 'type', + 'albumlabel', + 'artist', + 'genre', + 'rating', + 'title', + 'year', + 'thumbnail' + ], + 'sort': { + 'method': 'artist' + } + }, + 'success': function (data) { + if (data && data.result && data.result.albums) { + this.albumList = data.result.albums; + $.each($(this.albumList), jQuery.proxy(function (i, item) { + var floatableAlbum = this.generateThumb('album', item + .thumbnail, item.title, item.artist); + floatableAlbum.bind('click', { + album: item + }, jQuery.proxy(this.displayAlbumDetails, this)); + libraryContainer.append(floatableAlbum); + }, this)); + libraryContainer.append($('<div>').addClass('footerPadding')); + $('#spinner').hide(); + libraryContainer.bind('scroll', { + activeLibrary: libraryContainer + }, jQuery.proxy(this.updateScrollEffects, this)); + libraryContainer.trigger('scroll'); + myScroll = new iScroll('libraryContainer'); + } else { + libraryContainer.html(''); + } + } + }); + } else { + libraryContainer.show(); + libraryContainer.trigger('scroll'); + } + }, + getThumbnailPath: function (thumbnail) { + return thumbnail ? ('image/' + encodeURI(thumbnail)) : xbmc.core.DEFAULT_ALBUM_COVER; + }, + generateThumb: function (type, thumbnail, title, artist) { + title = title || ''; + artist = artist || ''; + + var showTitle = title, + showArtist = artist; + var floatableAlbum = $('<div>'); + var path = this.getThumbnailPath(thumbnail); + if (title.length > 21) { + showTitle = $.trim(title.substr(0, 18)) + '...'; + } + if (artist.length > 22) { + showArtist = $.trim(artist.substr(0, 20)) + '...'; + } + var className = ''; + var code = ''; + switch (type) { + case 'album': + className = 'floatableAlbum'; + code = '<p class="album" title="' + title + '">' + showTitle + '</p><p class="artist" title="' + + artist + '">' + artist + '</p>'; + break; + case 'movie': + className = 'floatableMovieCover'; + code = '<p class="album" title="' + title + '">' + showTitle + '</p>'; + break; + case 'tvshow': + className = 'floatableTVShowCover'; + break; + case 'tvshowseason': + className = 'floatableTVShowCoverSeason'; + break; + case 'image': + case 'directory': + className = 'floatableAlbum'; + code = '<p class="album" title="' + title + '">' + showTitle + '</p>'; + break; + case 'profile': + className = 'floatableProfileThumb'; + code = '<p class="album" title="' + title + '">' + showTitle + '</p>'; + break; + } + return floatableAlbum.addClass(className).html('<div class="imgWrapper"><div class="inner"><img src="' + + path + '" alt="' + title + '" /></div></div>' + code); + }, + showAlbumSelectorBlock: function (album) { + if (album) { + var prevAlbum = null, + nextAlbum = null; + $.each($(this.albumList), jQuery.proxy(function (i, item) { + if (item.albumid == album.albumid) { + if (this.albumList.length > 1) { + prevAlbum = this.albumList[i <= 0 ? this.albumList.length - 1 : i - 1]; + nextAlbum = this.albumList[i >= this.albumList.length ? 0 : i + 1]; + } + return false; /* .each break */ + } + }, this)); + var albumSelectorBlock = $('#albumSelector'); + if (!albumSelectorBlock || albumSelectorBlock.length === 0) { + albumSelectorBlock = $('<div>'); + albumSelectorBlock.attr('id', 'albumSelector') + .html( + '<table><tr><td class="allAlbums">All Albums</td><td class="activeAlbumTitle"></td>' + + '<td class="prevAlbum"> </td><td class="nextAlbum"> </td></tr></table>' + ); + $('#content').prepend(albumSelectorBlock); + $('#albumSelector .allAlbums').bind('click', jQuery.proxy(this.hideAlbumDetails, this)); + } + $('#albumSelector .prevAlbum').unbind(); + $('#albumSelector .nextAlbum').unbind(); + if (prevAlbum) { + $('#albumSelector .prevAlbum').bind('click', { + album: prevAlbum + }, jQuery.proxy(this.displayAlbumDetails, this)); + } + if (nextAlbum) { + $('#albumSelector .nextAlbum').bind('click', { + album: nextAlbum + }, jQuery.proxy(this.displayAlbumDetails, this)); + } + $('#albumSelector .activeAlbumTitle').html(album.title || 'Unknown Album'); + albumSelectorBlock.show(); + } + }, + hideAlbumDetails: function () { + $('.contentContainer').hide(); + this.musicLibraryOpen(); + }, + displayAlbumDetails: function (event) { + this.showAlbumSelectorBlock(event.data.album); + var albumDetailsContainer = $('#albumDetails' + event.data.album.albumid); + $('#topScrollFade').hide(); + if (!albumDetailsContainer || albumDetailsContainer.length === 0) { + $('#spinner').show(); + xbmc.rpc.request({ + 'context': this, + 'method': 'AudioLibrary.GetSongs', + 'params': { + 'properties': [ + 'title', + 'artist', + 'genre', + 'track', + 'duration', + 'year', + 'rating', + 'playcount' + ], + 'sort': { + 'method': 'track' + }, + 'filter': { + 'albumid': event.data.album.albumid + } + }, + 'success': function (data) { + albumDetailsContainer = $('<div>'); + albumDetailsContainer.attr('id', 'albumDetails' + event.data.album.albumid) + .addClass('contentContainer') + .addClass('albumContainer') + .html( + '<table class="albumView"><thead><tr class="headerRow"><th>Artwork</th><th> </th>' + + '<th>Name</th><th class="time">Time</th><th>Artist</th><th>Genre</th></tr></thead>' + + '<tbody class="resultSet"></tbody></table>' + ); + $('.contentContainer').hide(); + $('#content').append(albumDetailsContainer); + var albumThumbnail = event.data.album.thumbnail; + var albumTitle = event.data.album.title || 'Unknown Album'; + var albumArtist = event.data.album.artist.join(', ') || 'Unknown Artist'; + var trackCount = data.result.limits.total; + $.each($(data.result.songs), jQuery.proxy(function (i, item) { + var trackRow, trackNumberTD; + if (i === 0) { + trackRow = $('<tr>').addClass('trackRow').addClass('tr' + + i % 2); + trackRow.append($('<td>').attr('rowspan', ++trackCount + 1) + .addClass('albumThumb')); + for (var a = 0; a < 5; a++) { + trackRow.append($('<td>').html(' ').attr('style', + 'display: none')); + } + $('#albumDetails' + event.data.album.albumid + + ' .resultSet').append(trackRow); + } + trackRow = $('<tr>').addClass('trackRow').addClass('tr' + i % 2) + .bind('click', { + album: event.data.album, + itmnbr: i + }, jQuery.proxy(this.playTrack, this)); + trackNumberTD = $('<td>').html(item.track); + + trackRow.append(trackNumberTD); + var trackTitleTD = $('<td>').html(item.title); + + trackRow.append(trackTitleTD); + var trackDurationTD = $('<td>') + .addClass('time') + .html(xbmc.core.durationToString(item.duration)); + + trackRow.append(trackDurationTD); + var trackArtistTD = $('<td>').html(item.artist.join(', ')); + + trackRow.append(trackArtistTD); + var trackGenreTD = $('<td>').html(item.genre.join(', ')); + + trackRow.append(trackGenreTD); + $('#albumDetails' + event.data.album.albumid + ' .resultSet') + .append(trackRow); + }, this)); + if (trackCount > 0) { + var trackRow = $('<tr>').addClass('fillerTrackRow'), + i; + for (i = 0; i < 5; i++) { + trackRow.append($('<td>').html(' ')); + } + $('#albumDetails' + event.data.album.albumid + ' .resultSet').append( + trackRow); + + var trackRow2 = $('<tr>').addClass('fillerTrackRow2'); + trackRow2.append($('<td>').addClass('albumBG').html(' ')); + for (i = 0; i < 5; i++) { + trackRow2.append($('<td>').html(' ')); + } + $('#albumDetails' + event.data.album.albumid + ' .resultSet').append( + trackRow2); + } + $('#albumDetails' + event.data.album.albumid + ' .albumThumb') + .append(this.generateThumb('album', albumThumbnail, albumTitle, + albumArtist)) + .append($('<div>').addClass('footerPadding')); + $('#spinner').hide(); + myScroll = new iScroll('albumDetails' + event.data.album.albumid); + } + }); + } else { + $('.contentContainer').hide(); + $('#albumDetails' + event.data.album.albumid).show(); + } + }, + togglePosterView: function (event) { + var view = event.data.mode; + var wthumblist, hthumblist, hthumbdetails; + $("#toggleBanner").removeClass('activeMode'); + $("#togglePoster").removeClass('activeMode'); + $("#toggleLandscape").removeClass('activeMode'); + switch (view) { + case 'landscape': + xbmc.core.setCookie('TVView', 'landscape'); + wthumblist = '210px'; + hthumblist = '118px'; + hthumbdetails = '213px'; + $("#toggleLandscape").addClass('activeMode'); + break; + case 'banner': + xbmc.core.setCookie('TVView', 'banner'); + wthumblist = '379px'; + hthumblist = '70px'; + hthumbdetails = '70px'; + $("#toggleBanner").addClass('activeMode'); + break; + default: + xbmc.core.setCookie('TVView', 'poster'); + wthumblist = '135px'; + hthumblist = '199px'; + hthumbdetails = '559px'; + $("#togglePoster").addClass('activeMode'); + break; + } + $(".floatableTVShowCover, .floatableTVShowCover div.imgWrapper, .floatableTVShowCover img, " + + ".floatableTVShowCover div.imgWrapper div.inner") + .css('width', wthumblist).css('height', hthumblist); + $(".floatableTVShowCoverSeason div.imgWrapper, .floatableTVShowCoverSeason div.imgWrapper div.inner," + + ".floatableTVShowCoverSeason img, .floatableTVShowCoverSeason") + .css('height', hthumbdetails); + }, + displayTVShowDetails: function (event) { + var tvshowDetailsContainer = $('#tvShowDetails' + event.data.tvshow.tvshowid); + $('#topScrollFade').hide(); + toggle = this.toggle.detach(); + if (!tvshowDetailsContainer || tvshowDetailsContainer.length === 0) { + $('#spinner').show(); + xbmc.rpc.request({ + 'context': this, + 'method': 'VideoLibrary.GetSeasons', + 'params': { + 'properties': [ + 'season', + 'showtitle', + 'playcount', + 'episode', + 'thumbnail', + 'fanart' + ], + 'tvshowid': event.data.tvshow.tvshowid + }, + 'success': function (data) { + tvshowDetailsContainer = $('<div>'); + tvshowDetailsContainer.attr('id', 'tvShowDetails' + event.data.tvshow.tvshowid) + .css('display', 'none') + .addClass('contentContainer') + .addClass('tvshowContainer'); + var showThumb = this.generateThumb('tvshowseason', event.data.tvshow.thumbnail, + event.data.tvshow.title); + if (data && data.result && data.result.seasons && data.result.seasons.length > + 0) { + var showDetails = $('<div>').addClass('showDetails'); + showDetails.append(toggle); + showDetails.append($('<p>').html(data.result.seasons[0].showtitle).addClass( + 'showTitle')); + var seasonSelectionSelect = $('<select>').addClass('seasonPicker'); + this.tvActiveShowContainer = tvshowDetailsContainer; + $.each($(data.result.seasons), function (i, item) { + var season = $('<option>').attr('value', i); + season.text(item.label); + seasonSelectionSelect.append(season); + }); + seasonSelectionSelect.bind('change', { + tvshow: event.data.tvshow.tvshowid, + seasons: data.result.seasons, + element: seasonSelectionSelect + }, jQuery.proxy(this.displaySeasonListings, this)); + showDetails.append(seasonSelectionSelect); + tvshowDetailsContainer.append(showDetails); + tvshowDetailsContainer.append(showThumb); + seasonSelectionSelect.trigger('change'); + $('#content').append(tvshowDetailsContainer); + if (xbmc.core.getCookie('TVView') !== null && + xbmc.core.getCookie('TVView') !== 'banner' + ) { + var view = xbmc.core.getCookie('TVView'); + switch (view) { + case 'poster': + togglePoster.trigger('click'); + break; + case 'landscape': + toggleLandscape.trigger('click'); + break; + } + } + tvshowDetailsContainer.fadeIn(); + } + $('#spinner').hide(); + } + }); + } else { + $('.contentContainer').hide(); + $('#tvShowDetails' + event.data.tvshow.tvshowid).show(); + $('#tvShowDetails' + event.data.tvshow.tvshowid + ' select').trigger('change'); + } + }, + displaySeasonListings: function (event) { + var selectedVal = event.data.element.val(); + var seasons = event.data.seasons; + $('#topScrollFade').hide(); + var oldListings = $('.episodeListingsContainer', this.tvActiveShowContainer).fadeOut(); + this.tvActiveSeason = selectedVal; + xbmc.rpc.request({ + 'context': this, + 'method': 'VideoLibrary.GetEpisodes', + 'params': { + 'properties': [ + 'title', + 'thumbnail', + 'episode', + 'plot', + 'season' + ], + 'season': seasons[selectedVal].season, + 'tvshowid': event.data.tvshow + }, + 'success': function (data) { + var episodeListingsContainer = $('<div>').addClass('episodeListingsContainer'); + var episodeTable = $('<table>').addClass('seasonView').html( + '<thead><tr class="headerRow"><th class="thumbHeader">N°</th><th>Title</th>' + + '<th class="thumbHeader">Thumb</th><th class="thumbHeader">Details</th></tr></thead>' + + '<tbody class="resultSet"></tbody>' + ); + $.each($(data.result.episodes), jQuery.proxy(function (i, item) { + var episodeRow = $('<tr>').addClass('episodeRow').addClass('tr' + + i % 2); + var episodePictureImg = $('<img>').bind('click', { + episode: item + }, jQuery.proxy(this.playTVShow, this)).css('cursor', 'pointer'); + episodePictureImg.attr('src', this.getThumbnailPath(item + .thumbnail)); + var episodePicture = $('<td>').addClass('episodeThumb').append( + episodePictureImg).bind('click', { + episode: item + }, jQuery.proxy(this.playTVShow, this)); + var episodeNumber = $('<td>').addClass('episodeNumber').html(item + .episode).bind('click', { + episode: item + }, jQuery.proxy(this.playTVShow, this)); + var episodeTitle = $('<td>').html(item.title).bind('click', { + episode: item + }, jQuery.proxy(this.playTVShow, this)); + var episodeDetails = $('<td class="info">').html('').bind('click', { + episode: item + }, jQuery.proxy(this.displayEpisodeDetails, this)).css('cursor', + 'pointer'); + episodeRow.append(episodeNumber).append(episodeTitle).append( + episodePicture).append(episodeDetails); + episodeTable.append(episodeRow); + }, this)); + episodeListingsContainer.append(episodeTable); + $(this.tvActiveShowContainer).append(episodeListingsContainer); + } + }); + }, + displayEpisodeDetails: function (event) { + var episodeDetails = $('<div>').attr('id', 'episode-' + event.data.episode.episodeid).addClass( + 'episodePopoverContainer'); + episodeDetails.append($('<img>').attr('src', 'images/close-button.png').addClass('closeButton').bind( + 'click', jQuery.proxy(this.hideOverlay, this))); + episodeDetails.append($('<img>').attr('src', this.getThumbnailPath(event.data.episode.thumbnail)) + .addClass('episodeCover')); + episodeDetails.append($('<div>').addClass('playIcon').bind('click', { + episode: event.data.episode + }, jQuery.proxy(this.playTVShow, this))); + var episodeTitle = $('<p>').addClass('episodeTitle'); + var yearText = event.data.episode.year ? ' <span class="year">(' + event.data.episode.year + + ')</span>' : ''; + episodeTitle.html(event.data.episode.title + yearText); + episodeDetails.append(episodeTitle); + if (event.data.episode.runtime) { + episodeDetails.append($('<p>').addClass('runtime').html('<strong>Runtime:</strong> ' + Math.ceil( + event.data.episode.runtime / 60) + ' minutes')); + } + if (event.data.episode.season) { + episodeDetails.append($('<p>').addClass('season').html('<strong>Season:</strong> ' + event.data + .episode.season)); + } + if (event.data.episode.episode) { + episodeDetails.append($('<p>').addClass('episode').html('<strong>Episode:</strong> ' + event.data + .episode.episode)); + } + if (event.data.episode.plot) { + episodeDetails.append($('<p>').addClass('plot').html('<strong>Plot:</strong> <br/><br/>' + event + .data.episode.plot)); + } + if (event.data.episode.genre) { + episodeDetails.append($('<p>').addClass('genre').html('<strong>Genre:</strong> ' + event.data + .episode.genre)); + } + if (event.data.episode.director) { + episodeDetails.append($('<p>').addClass('director').html('<strong>Directed By:</strong> ' + event + .data.episode.director)); + } + this.activeCover = episodeDetails; + $('body').append(episodeDetails); + $('#overlay').show(); + this.updatePlayButtonLocation(); + }, + playTVShow: function (event) { + xbmc.rpc.request({ + 'context': this, + 'method': 'Player.Open', + 'params': { + 'item': { + 'episodeid': event.data.episode.episodeid + } + }, + 'success': function (data) { + this.hideOverlay(); + } + }); + }, + hideOverlay: function (event) { + if (this.activeCover) { + $(this.activeCover).remove(); + this.activeCover = null; + } + $('#overlay').hide(); + }, + updatePlayButtonLocation: function (event) { + var movieContainer = $('.movieCover'), + playIcon; + if (movieContainer.length > 0) { + playIcon = $('.playIcon'); + if (playIcon.length > 0) { + var heightpi = $(movieContainer[0]).height(); + playIcon.width(Math.floor(0.65 * heightpi)); + playIcon.height(heightpi); + } + } + var episodeContainer = $('.episodeCover'); + if (episodeContainer.length > 0) { + playIcon = $('.playIcon'); + if (playIcon.length > 0) { + var widthpi = $(episodeContainer[0]).width(); + playIcon.width(widthpi); + //assume 16/9 thumb + playIcon.height(Math.floor(widthpi * 9 / 16)); + } + } + }, + playMovie: function (event) { + xbmc.rpc.request({ + 'context': this, + 'method': 'Player.Open', + 'params': { + 'item': { + 'movieid': event.data.movie.movieid + } + }, + 'success': function (data) { + this.hideOverlay(); + } + }); + }, + displayMovieDetails: function (event) { + var movieDetails = $('<div>').attr('id', 'movie-' + event.data.movie.movieid).addClass( + 'moviePopoverContainer'); + movieDetails.append($('<img>').attr('src', 'images/close-button.png').addClass('closeButton').bind( + 'click', jQuery.proxy(this.hideOverlay, this))); + movieDetails.append($('<img>').attr('src', this.getThumbnailPath(event.data.movie.thumbnail)).addClass( + 'movieCover')); + movieDetails.append($('<div>').addClass('playIcon').bind('click', { + movie: event.data.movie + }, jQuery.proxy(this.playMovie, this))); + var movieTitle = $('<p>').addClass('movieTitle'); + var yearText = event.data.movie.year ? ' <span class="year">(' + event.data.movie.year + ')</span>' : + ''; + movieTitle.html(event.data.movie.title + yearText); + movieDetails.append(movieTitle); + if (event.data.movie.runtime) { + movieDetails.append($('<p>').addClass('runtime').html('<strong>Runtime:</strong> ' + Math.ceil(event + .data.movie.runtime / 60) + ' minutes')); + } + if (event.data.movie.plot) { + movieDetails.append($('<p>').addClass('plot').html(event.data.movie.plot)); + } + if (event.data.movie.genre) { + movieDetails.append($('<p>').addClass('genre').html('<strong>Genre:</strong> ' + event.data.movie + .genre)); + } + if (event.data.movie.director) { + movieDetails.append($('<p>').addClass('director').html('<strong>Directed By:</strong> ' + event.data + .movie.director)); + } + this.activeCover = movieDetails; + $('body').append(movieDetails); + $('#overlay').show(); + this.updatePlayButtonLocation(); + }, + playTrack: function (event) { + xbmc.rpc.request({ + 'context': this, + 'method': 'Playlist.Clear', + 'params': { + 'playlistid': this.playlists["audio"] + }, + 'success': function (data) { + xbmc.rpc.request({ + 'context': this, + 'method': 'Playlist.Add', + 'params': { + 'playlistid': this.playlists["audio"], + 'item': { + 'albumid': event.data.album.albumid + } + }, + 'success': function (data) { + xbmc.rpc.request({ + 'method': 'Player.Open', + 'params': { + 'item': { + 'playlistid': this.playlists["audio"], + 'position': event.data.itmnbr + } + }, + 'success': function () {} + }); + } + }); + } + }); + }, + loadProfile: function (event) { + return xbmc.rpc.request({ + 'context': this, + 'method': 'Profiles.LoadProfile', + 'params': { + 'profile': event.data.profile.label + } + }); + }, + movieLibraryOpen: function () { + this.resetPage(); + $('#movieLibrary').addClass('selected'); + $('.contentContainer').hide(); + var libraryContainer = $('#movieLibraryContainer'); + if (!libraryContainer || libraryContainer.length === 0) { + $('#spinner').show(); + xbmc.rpc.request({ + 'context': this, + 'method': 'VideoLibrary.GetMovies', + 'params': { + 'limits': { + 'start': 0 + }, + 'properties': [ + 'genre', + 'director', + 'trailer', + 'tagline', + 'plot', + 'plotoutline', + 'title', + 'originaltitle', + 'lastplayed', + 'runtime', + 'year', + 'playcount', + 'rating', + 'thumbnail', + 'file' + ], + 'sort': { + 'method': 'sorttitle', + 'ignorearticle': true + } + }, + 'success': function (data) { + if (data && data.result && data.result.movies) { + libraryContainer = $('<div>'); + libraryContainer.attr('id', 'movieLibraryContainer') + .addClass('contentContainer'); + $('#content').append(libraryContainer); + } else { + libraryContainer.html(''); + } + $.each($(data.result.movies), jQuery.proxy(function (i, item) { + var floatableMovieCover = this.generateThumb('movie', item + .thumbnail, item.title); + floatableMovieCover.bind('click', { + movie: item + }, jQuery.proxy(this.displayMovieDetails, this)); + libraryContainer.append(floatableMovieCover); + }, this)); + libraryContainer.append($('<div>').addClass('footerPadding')); + $('#spinner').hide(); + libraryContainer.bind('scroll', { + activeLibrary: libraryContainer + }, jQuery.proxy(this.updateScrollEffects, this)); + libraryContainer.trigger('scroll'); + myScroll = new iScroll('movieLibraryContainer'); + } + }); + } else { + libraryContainer.show(); + libraryContainer.trigger('scroll'); + } + }, + tvshowLibraryOpen: function () { + this.resetPage(); + $('#tvshowLibrary').addClass('selected'); + $('.contentContainer').hide(); + var libraryContainer = $('#tvshowLibraryContainer'); + if (!libraryContainer || libraryContainer.length === 0) { + $('#spinner').show(); + toggle = $('<p>').addClass('toggle'); + togglePoster = $('<span>Poster</span>'); + togglePoster.attr('id', 'togglePoster') + .css('cursor', 'pointer') + .bind('click', { + mode: 'poster' + }, jQuery.proxy(this.togglePosterView, this)); + toggleBanner = $('<span>Banner</span>'); + toggleBanner.attr('id', 'toggleBanner') + .css('cursor', 'pointer') + .addClass('activeMode') + .bind('click', { + mode: 'banner' + }, jQuery.proxy(this.togglePosterView, this)); + toggleLandscape = $('<span>Landscape</span>'); + toggleLandscape.attr('id', 'toggleLandscape') + .css('cursor', 'pointer') + .bind('click', { + mode: 'landscape' + }, jQuery.proxy(this.togglePosterView, this)); + toggle.append(toggleBanner).append(' | ').append(togglePoster).append(' | ').append( + toggleLandscape); + this.toggle = toggle; + xbmc.rpc.request({ + 'context': this, + 'method': 'VideoLibrary.GetTVShows', + 'params': { + 'properties': [ + 'genre', + 'plot', + 'title', + 'lastplayed', + 'episode', + 'year', + 'playcount', + 'rating', + 'thumbnail', + 'studio', + 'mpaa', + 'premiered' + ], + 'sort': { + 'method': 'sorttitle', + 'ignorearticle': true + } + }, + 'success': function (data) { + if (data && data.result && data.result.tvshows) { + libraryContainer = $('<div>'); + libraryContainer.append(toggle); + libraryContainer.attr('id', 'tvshowLibraryContainer') + .addClass('contentContainer'); + $('#content').append(libraryContainer); + } else { + libraryContainer.html(''); + } + $.each($(data.result.tvshows), jQuery.proxy(function (i, item) { + var floatableTVShowCover = this.generateThumb('tvshow', item + .thumbnail, item.title); + floatableTVShowCover.bind('click', { + tvshow: item + }, jQuery.proxy(this.displayTVShowDetails, this)); + libraryContainer.append(floatableTVShowCover); + }, this)); + libraryContainer.append($('<div>').addClass('footerPadding')); + $('#spinner').hide(); + libraryContainer.bind('scroll', { + activeLibrary: libraryContainer + }, jQuery.proxy(this.updateScrollEffects, this)); + libraryContainer.trigger('scroll'); + myScroll = new iScroll('tvshowLibraryContainer'); + if (xbmc.core.getCookie('TVView') !== null && + xbmc.core.getCookie('TVView') !== 'banner' + ) { + var view = xbmc.core.getCookie('TVView'); + switch (view) { + case 'poster': + togglePoster.trigger('click'); + break; + case 'landscape': + toggleLandscape.trigger('click'); + break; + } + } + } + }); + } else { + libraryContainer.prepend($(".toggle").detach()).show(); + libraryContainer.trigger('scroll'); + } + }, + profilesOpen: function () { + this.resetPage(); + $('#profiles').addClass('selected'); + $('.contentContainer').hide(); + var libraryContainer = $('#profilesContainer'); + if (!libraryContainer || libraryContainer.length == 0) { + $('#spinner').show(); + var currentProfile = ""; + xbmc.rpc.request({ + 'method': 'Profiles.GetCurrentProfile', + 'params': { + 'properties': [ + 'lockmode' + ] + }, + 'success': function (data) { + if (data) + if (data.result) + currentProfile = data.result.label; + } + }); + xbmc.rpc.request({ + 'context': this, + 'method': 'Profiles.GetProfiles', + 'params': { + 'limits': { + 'start': 0 + }, + 'properties': [ + 'thumbnail' + ], + 'sort': { + 'method': 'sorttitle', + 'ignorearticle': true + } + }, + 'success': function (data) { + if (data && data.result && data.result.profiles) { + libraryContainer = $('<div>'); + libraryContainer.attr('id', 'profilesContainer') + .addClass('contentContainer'); + $('#content').append(libraryContainer); + } else { + libraryContainer.html(''); + } + $.each($(data.result.profiles), jQuery.proxy(function (i, item) { + var itemLabel = item.label; + if (currentProfile == itemLabel) { + itemLabel = itemLabel + "*"; + } + var floatableProfileThumb = this.generateThumb('profile', item + .thumbnail, itemLabel); + floatableProfileThumb.bind('click', { + profile: item + }, jQuery.proxy(this.loadProfile, this)); + libraryContainer.append(floatableProfileThumb); + }, this)); + libraryContainer.append($('<div>').addClass('footerPadding')); + $('#spinner').hide(); + libraryContainer.bind('scroll', { + activeLibrary: libraryContainer + }, jQuery.proxy(this.updateScrollEffects, this)); + libraryContainer.trigger('scroll'); + myScroll = new iScroll('profilesContainer'); + } + }); + } else { + libraryContainer.show(); + libraryContainer.trigger('scroll'); + } + }, + updateScrollEffects: function (event) { + if (event.data.activeLibrary && $(event.data.activeLibrary).scrollTop() > 0) { + $('#topScrollFade').fadeIn(); + } else { + $('#topScrollFade').fadeOut(); + } + }, + startSlideshow: function (event) { + xbmc.rpc.request({ + 'method': 'Player.Open', + 'params': { + 'item': { + 'recursive': 'true', + 'random': 'true', + 'path': this.replaceAll(event.data.directory.file, "\\", "\\\\") + } + }, + 'success': function () {} + }); + }, + showDirectory: function (event) { + var directory = event.data.directory.file; + var jsonDirectory = this.replaceAll(directory, "\\", "\\\\"); + this.resetPage(); + $('#pictureLibrary').addClass('selected'); + $('.contentContainer').hide(); + var libraryContainer = $('#pictureLibraryDirContainer' + directory); + if (!libraryContainer || libraryContainer.length === 0) { + $('#spinner').show(); + xbmc.rpc.request({ + 'context': this, + 'method': 'Files.GetDirectory', + 'params': { + 'media': 'pictures', + 'directory': jsonDirectory + }, + 'success': function (data) { + if (data && data.result && (data.result.directories || data.result.files)) { + libraryContainer = $('<div>'); + libraryContainer.attr('id', 'pictureLibraryDirContainer' + directory) + .addClass('contentContainer'); + $('#content').append(libraryContainer); + var breadcrumb = $('<div>'); + var seperator = '/'; + var item = ''; + var directoryArray = directory.split(seperator); + jQuery.each(directoryArray, function (i, v) { + if (v !== '') { + item += v + seperator; + breadcrumb.append($('<div>').text(' > ' + v).css('float', + 'left').addClass('breadcrumb')); + } + }); + libraryContainer.append(breadcrumb); + libraryContainer.append($('<div>').css('clear', 'both')); + if (data.result.files) { + $.each($(data.result.files), jQuery.proxy(function (i, item) { + if (item.filetype == "file") { + var floatableImage = this.generateThumb('image', + item.file, item.label); + libraryContainer.append(floatableImage); + } else if (item.filetype == "directory") { + var floatableShare = this.generateThumb('directory', + item.thumbnail, item.label); + floatableShare.bind('click', { + directory: item + }, jQuery.proxy(this.showDirectory, this)); + libraryContainer.append(floatableShare); + } + }, this)); + } + libraryContainer.append($('<div>').addClass('footerPadding')); + } else { + libraryContainer.html(''); + } + $('#spinner').hide(); + libraryContainer.bind('scroll', { + activeLibrary: libraryContainer + }, jQuery.proxy(this.updateScrollEffects, this)); + libraryContainer.trigger('scroll'); + myScroll = new iScroll('#pictureLibraryDirContainer' + directory); + } + }); + } else { + libraryContainer.show(); + libraryContainer.trigger('scroll'); + } + }, + pictureLibraryOpen: function () { + this.resetPage(); + $('#pictureLibrary').addClass('selected'); + $('.contentContainer').hide(); + var libraryContainer = $('#pictureLibraryContainer'); + if (!libraryContainer || libraryContainer.length === 0) { + $('#spinner').show(); + xbmc.rpc.request({ + 'context': this, + 'method': 'Files.GetSources', + 'params': { + 'media': 'pictures' + }, + 'success': function (data) { + if (data && data.result && data.result.shares) { + libraryContainer = $('<div>'); + libraryContainer.attr('id', 'pictureLibraryContainer') + .addClass('contentContainer'); + $('#content').append(libraryContainer); + } else { + libraryContainer.html(''); + } + $.each($(data.result.shares), jQuery.proxy(function (i, item) { + var floatableShare = this.generateThumb('directory', item + .thumbnail, item.label); + floatableShare.bind('click', { + directory: item + }, jQuery.proxy(this.showDirectory, this)); + libraryContainer.append(floatableShare); + }, this)); + libraryContainer.append($('<div>').addClass('footerPadding')); + $('#spinner').hide(); + libraryContainer.bind('scroll', { + activeLibrary: libraryContainer + }, jQuery.proxy(this.updateScrollEffects, this)); + libraryContainer.trigger('scroll'); + myScroll = new iScroll('#pictureLibraryContainer'); + } + }); + } else { + libraryContainer.show(); + libraryContainer.trigger('scroll'); + } + } +}; diff --git a/debian/webinterface-default/js/NowPlayingManager.js b/debian/webinterface-default/js/NowPlayingManager.js new file mode 100755 index 0000000..fb34f74 --- /dev/null +++ b/debian/webinterface-default/js/NowPlayingManager.js @@ -0,0 +1,661 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/>. + * + */ + +var NowPlayingManager = function () { + this.init(); + return true; +} + +NowPlayingManager.prototype = { + updateCounter: 0, + activePlayer: "", + activePlayerId: -1, + currentItem: -1, + playing: false, + paused: false, + playlistid: -1, + + init: function () { + $('#pbPause').hide(); /* Assume we are not playing something */ + this.bindPlaybackControls(); + this.updateState(); + $('#nextTrack').bind('click', jQuery.proxy(this.showPlaylist, this)); + $('#nowPlayingPlaylist').bind('click', function () { + return false; + }); + $(window).bind('click', jQuery.proxy(this.hidePlaylist, this)); + }, + updateState: function () { + xbmc.rpc.request({ + 'context': this, + 'method': 'Player.GetActivePlayers', + 'timeout': 3000, + 'success': function (data) { + if (data && data.result && data.result.length > 0) { + if (data.result[0].playerid != this.activePlayerId) { + this.activePlayerId = data.result[0].playerid; + this.activePlayer = data.result[0].type; + if (this.activePlayer == "audio") { + this.stopVideoPlaylistUpdate(); + this.displayAudioNowPlaying(); + } else if (this.activePlayer == "video") { + this.stopAudioPlaylistUpdate(); + this.displayVideoNowPlaying(); + } else { + this.stopVideoPlaylistUpdate(); + this.stopAudioPlaylistUpdate(); + this.activePlayer = ""; + this.activePlayerId = -1; + } + + this.stopRefreshTime(); + this.updatePlayer(); + } + } else if (!data || !data.result || data.result.length <= 0) { + this.stopVideoPlaylistUpdate(); + this.stopAudioPlaylistUpdate(); + this.activePlayer = ""; + this.activePlayerId = -1; + } + + if (this.activePlayerId >= 0) { + this.showFooter(); + } else { + this.stopRefreshTime(); + this.hideFooter(); + } + + setTimeout(jQuery.proxy(this.updateState, this), 1000); + }, + 'error': function (data, error) { + xbmc.core.displayCommunicationError(); + setTimeout(jQuery.proxy(this.updateState, this), 2000); + } + }); + }, + updatePlayer: function () { + xbmc.rpc.request({ + 'context': this, + 'method': 'Player.GetProperties', + 'params': { + 'playerid': this.activePlayerId, + 'properties': [ + 'playlistid', + 'speed', + 'position', + 'totaltime', + 'time' + ] + }, + 'success': function (data) { + if (data && data.result) { + this.playlistid = data.result.playlistid; + this.playing = data.result.speed != 0; + this.paused = data.result.speed == 0; + this.currentItem = data.result.position; + this.trackBaseTime = xbmc.core.timeToDuration(data.result.time); + this.trackDurationTime = xbmc.core.timeToDuration(data.result.totaltime); + if (!this.autoRefreshAudioData && !this.autoRefreshVideoData && this.playing) { + if (this.activePlayer == 'audio') { + this.autoRefreshAudioData = true; + this.refreshAudioData(); + } else if (this.activePlayer == 'video') { + this.autoRefreshVideoData = true; + this.refreshVideoData(); + } + } + } + if ((this.autoRefreshAudioData || this.autoRefreshVideoData) && !this + .activeItemTimer) { + this.activeItemTimer = 1; + setTimeout(jQuery.proxy(this.updateActiveItemDurationLoop, this), 1000); + } + } + }); + }, + bindPlaybackControls: function () { + $('#pbNext').bind('click', jQuery.proxy(this.nextTrack, this)); + $('#pbPrev').bind('click', jQuery.proxy(this.prevTrack, this)); + $('#pbStop').bind('click', jQuery.proxy(this.stopTrack, this)); + $('#pbPlay').bind('click', jQuery.proxy(this.playPauseTrack, this)); + $('#pbPause').bind('click', jQuery.proxy(this.playPauseTrack, this)); + that = this + $(document).keypress(function (event) { + switch (event.which) { + case 32: //spacebar + event.preventDefault() + jQuery.proxy(that.playPauseTrack, that)(); + break; + case 120: //x key + event.preventDefault() + jQuery.proxy(that.stopTrack, that)(); + break; + case 44: //period key + event.preventDefault() + jQuery.proxy(that.nextTrack, that)(); + break; + case 46: //comma key + event.preventDefault() + jQuery.proxy(that.prevTrack, that)(); + break; + } + }); + }, + showPlaylist: function () { + $('#nextText').html('Playlist: '); + $('#nowPlayingPlaylist').show(); + return false; + }, + hidePlaylist: function () { + $('#nextText').html('Next: '); + $('#nowPlayingPlaylist').hide(); + return false; + }, + hideFooter: function () { + $('#footerPopover').hide(); + $('#overlay').css('bottom', '0px'); + }, + showFooter: function () { + $('#footerPopover').show(); + $('#overlay').css('bottom', '150px'); + }, + nextTrack: function () { + if (this.activePlayer) { + xbmc.rpc.request({ + 'method': 'Player.GoTo', + 'params': { + 'playerid': this.activePlayerId, + 'to': 'next' + }, + 'success': function () {} + }); + } + }, + prevTrack: function () { + if (this.activePlayer) { + xbmc.rpc.request({ + 'method': 'Player.GoTo', + 'params': { + 'playerid': this.activePlayerId, + 'to': 'previous' + }, + 'success': function () {} + }); + } + }, + stopTrack: function () { + if (this.activePlayer) { + xbmc.rpc.request({ + 'context': this, + 'method': 'Player.Stop', + 'params': { + 'playerid': this.activePlayerId + }, + 'success': function (data) { + if (data && data.result == 'OK') { + this.playing = false; + this.paused = false; + this.trackBaseTime = 0; + this.trackDurationTime = 0; + this.showPlayButton(); + } + } + }); + } + }, + playPauseTrack: function () { + if (this.activePlayer) { + var method = ((this.playing || this.paused) ? 'Player.PlayPause' : 'Playlist.Play'); + xbmc.rpc.request({ + 'context': this, + 'method': method, + 'params': { + 'playerid': this.activePlayerId + }, + 'success': function (data) { + if (data && data.result) { + this.playing = data.result.speed != 0; + this.paused = data.result.speed == 0; + if (this.playing) { + this.showPauseButton(); + } else { + this.showPlayButton(); + } + } + } + }); + } + }, + showPauseButton: function () { + $('#pbPause').show(); + $('#pbPlay').hide(); + }, + showPlayButton: function () { + $('#pbPause').hide(); + $('#pbPlay').show(); + }, + displayAudioNowPlaying: function () { + if (!this.autoRefreshAudioPlaylist) { + this.autoRefreshAudioPlaylist = true; + this.updateAudioPlaylist(); + } + }, + displayVideoNowPlaying: function () { + if (!this.autoRefreshVideoPlaylist) { + this.autoRefreshVideoPlaylist = true; + this.updateVideoPlaylist(); + } + }, + playPlaylistItem: function (sender) { + var sequenceId = $(sender.currentTarget).attr('seq'); + if (!this.activePlaylistItem || (this.activePlaylistItem !== undefined && sequenceId != this + .activePlaylistItem.seq)) { + xbmc.rpc.request({ + 'method': 'Player.GoTo', + 'params': { + 'playerid': this.activePlayerId, + 'to': sequenceId + }, + 'success': function () {} + }); + } + this.hidePlaylist(); + }, + playlistChanged: function (newPlaylist) { + if (this.activePlaylist && !newPlaylist || !this.activePlaylist && newPlaylist) { + return true; + } + if (!this.activePlaylist && !newPlaylist) { + return false; + } + if (this.activePlaylist.length != newPlaylist.length) { + return true; + } + for (var i = 0; i < newPlaylist.length; i++) { + if (!this.comparePlaylistItems(this.activePlaylist[i], newPlaylist[i])) { + return true; + } + } + return false; + }, + updateAudioPlaylist: function () { + xbmc.rpc.request({ + 'context': this, + 'method': 'Playlist.GetItems', + 'params': { + 'playlistid': this.playlistid, + 'properties': [ + 'title', + 'album', + 'artist', + 'duration', + 'thumbnail' + ] + }, + 'success': function (data) { + if (data && data.result && data.result.items && data.result.items.length > 0 && data + .result.limits.total > 0) { + if (!this.activePlaylistItem || this.playlistChanged(data.result.items) || (this + .activePlaylistItem && (this.activePlaylistItem.seq != this.currentItem) + )) { + var ul = $('<ul>'); + var activeItem; + $.each($(data.result.items), jQuery.proxy(function (i, item) { + var li = $('<li>'); + var code = '<span class="duration">' + xbmc.core + .durationToString(item.duration) + + '</span><div class="trackInfo" title="' + item.title + + ' - ' + item.artist + '"><span class="trackTitle">' + + item.title + '</span> - <span class="trackArtist">' + + item.artist + '</span></div>'; + if (i == this.currentItem) { + activeItem = item; + activeItem.seq = i; + li.addClass('activeItem'); + } + if (i == (this.currentItem + 1)) { + $('#nextTrack').html(code).show(); + } + li.bind('click', jQuery.proxy(this.playPlaylistItem, this)); + ul.append(li.attr('seq', i).html(code)); + }, this)); + if (data.result.limits.total > 1) { + if (activeItem && data.result.limits.total - 1 == activeItem.seq) { + $('#nextTrack').html( + '<div class="trackInfo">Last track in playlist</div>') + .show(); + } + $('#nextText').show(); + $('#nowPlayingPlaylist').html('').append(ul); + } else { + $('#nextText').hide(); + $('#nowPlayingPlaylist').hide(); + $('#nextTrack').hide(); + } + if (!this.comparePlaylistItems(activeItem, this.activePlaylistItem)) { + this.activePlaylistItem = activeItem; + if (!this.updateActiveItemDurationRunOnce) { + this.updateActiveItemDurationRunOnce = true; + this.updatePlayer(); + } + } else if (!activeItem) { + this.stopRefreshTime(); + } + this.activePlaylist = data.result.items; + $('#videoDescription').hide(); + $('#audioDescription').show(); + $('#nowPlayingPanel').show(); + } + } else { + this.activePlaylist = null; + $('#audioDescription').hide(); + $('#nowPlayingPanel').hide(); + } + if (this.autoRefreshAudioPlaylist) { + setTimeout(jQuery.proxy(this.updateAudioPlaylist, this), 1000); + } + }, + 'error': function (data) { + xbmc.core.displayCommunicationError(); + if (this.autoRefreshAudioPlaylist) { + setTimeout(jQuery.proxy(this.updateAudioPlaylist, this), + 2000); /* Slow down request period */ + } + } + }); + }, + stopAudioPlaylistUpdate: function () { + this.autoRefreshAudioPlaylist = false; + this.updateActiveItemDurationRunOnce = false; + }, + stopVideoPlaylistUpdate: function () { + this.autoRefreshVideoPlaylist = false; + this.updateActiveItemDurationRunOnce = false; + }, + updateActiveItemDurationLoop: function () { + this.activeItemTimer = 0; + this.updatePlayer(); + }, + refreshAudioDataLoop: function () { + this.audioRefreshTimer = 0; + this.refreshAudioData(); + }, + refreshAudioData: function () { + if (this.autoRefreshAudioData && !this.audioRefreshTimer) { + this.audioRefreshTimer = 1; + setTimeout(jQuery.proxy(this.refreshAudioDataLoop, this), 1000); + } + if (this.playing && !this.paused) { + this.trackBaseTime++; + } + if (this.paused) { + this.showPlayButton(); + } else if (this.playing) { + this.showPauseButton(); + } + if (this.activePlaylistItem) { + if (this.activePlaylistItem != this.lastPlaylistItem) { + this.lastPlaylistItem = this.activePlaylistItem; + var imgPath = xbmc.core.DEFAULT_ALBUM_COVER; + if (this.activePlaylistItem.thumbnail) { + imgPath = 'image/' + encodeURI(this.activePlaylistItem.thumbnail); + } + $('#audioCoverArt').html('<img src="' + imgPath + '" alt="' + this.activePlaylistItem.album + + ' cover art">'); + $('#audioTrackTitle').html('<span title="' + this.activePlaylistItem.title + '">' + this + .activePlaylistItem.title + '</span>'); + if (this.activePlaylistItem.album) { + $('#audioAlbumTitle').html('<span title="' + this.activePlaylistItem.album + '">' + this + .activePlaylistItem.album + '</span>') + .show(); + } else { + $('#audioAlbumTitle').hide(); + } + $('#audioArtistTitle').html(this.activePlaylistItem.artist); + $('#progressBar').attr('style', ''); + } + $('#audioDuration').html(xbmc.core.durationToString(this.trackBaseTime) + ' / ' + xbmc.core + .durationToString(this.trackDurationTime)); + var buttonWidth = $('#progressBar .progressIndicator').width(); + var progressBarWidth = (this.trackBaseTime / this.trackDurationTime) * 100; + var progressSliderPosition = Math.ceil(($('#progressBar').width() / 100) * progressBarWidth) - + buttonWidth; + if (progressSliderPosition < 0) { + progressSliderPosition = 0; + } + if (progressBarWidth <= 100) { + $('#progressBar .elapsedTime').width(progressBarWidth + '%'); + $('#progressBar .progressIndicator').css('left', progressSliderPosition); + } + } + }, + refreshVideoDataLoop: function () { + this.videoRefreshTimer = 0; + this.refreshVideoData(); + }, + refreshVideoData: function () { + if (this.autoRefreshVideoData && !this.videoRefreshTimer) { + this.videoRefreshTimer = 1; + setTimeout(jQuery.proxy(this.refreshVideoDataLoop, this), 1500); + } + if (this.playing && !this.paused) { + this.trackBaseTime++; + } + if (this.paused) { + this.showPlayButton(); + } else if (this.playing) { + this.showPauseButton(); + } + if (this.activePlaylistItem) { + if (this.activePlaylistItem != this.lastPlaylistItem) { + this.lastPlaylistItem = this.activePlaylistItem; + var imgPath = xbmc.core.DEFAULT_VIDEO_COVER; + if (this.activePlaylistItem.thumbnail) { + imgPath = 'image/' + encodeURI(this.activePlaylistItem.thumbnail); + } + $('#videoCoverArt').html('<img src="' + imgPath + '" alt="' + this.activePlaylistItem.title + + ' cover art">'); + $('#videoShowTitle').html(this.activePlaylistItem.showtitle || ' '); + var extra = ''; + if (this.activePlaylistItem.season >= 0 && this.activePlaylistItem.episode >= 0) { + extra = this.activePlaylistItem.season + 'x' + this.activePlaylistItem.episode + ' '; + } + $('#videoTitle').html(extra + this.activePlaylistItem.title); + } + $('#videoDuration').html(xbmc.core.durationToString(this.trackBaseTime) + ' / ' + xbmc.core + .durationToString(this.trackDurationTime)); + var buttonWidth = $('#progressBar .progressIndicator').width(); + var progressBarWidth = (this.trackBaseTime / this.trackDurationTime) * 100; + var progressSliderPosition = Math.ceil(($('#progressBar').width() / 100) * progressBarWidth) - + buttonWidth; + if (progressSliderPosition < 0) { + progressSliderPosition = 0; + } + if (progressBarWidth <= 100) { + $('#progressBar .elapsedTime').width(progressBarWidth + '%'); + $('#progressBar .progressIndicator').css('left', progressSliderPosition); + } + } + }, + stopRefreshTime: function () { + this.autoRefreshAudioData = false; + this.autoRefreshVideoData = false; + }, + comparePlaylistItems: function (item1, item2) { + if (!item1 || !item2) { + if (!item1 && !item2) { + return true; + } + return false; + } + if (item1.title != item2.title) { + return false; + } + if (item1.album != item2.album) { + return false; + } + if (item1.artist != item2.artist) { + return false; + } + if (item1.duration != item2.duration) { + return false; + } + if (item1.label != item2.label) { + return false; + } + if (item1.season != item2.season) { + return false; + } + if (item1.episode != item2.episode) { + return false; + } + return true; + }, + updateVideoPlaylist: function () { + xbmc.rpc.request({ + 'context': this, + 'method': 'Playlist.GetItems', + 'params': { + 'playlistid': this.playlistid, + 'properties': [ + 'title', + 'season', + 'episode', + 'plot', + 'runtime', + 'showtitle', + 'thumbnail' + ] + }, + 'success': function (data) { + if (data && data.result && data.result.items && data.result.items.length > 0 && data + .result.limits.total > 0) { + if (this.playlistChanged(data.result.items)) { + var ul = $('<ul>'); + var activeItem; + $.each($(data.result.items), jQuery.proxy(function (i, item) { + var li = $('<li>'); + var extra = ''; + if (item.season >= 0 && item.episode >= 0) { + extra = item.season + 'x' + item.episode + ' '; + } + var code = '<span class="duration">' + xbmc.core + .durationToString(item.runtime) + + '</span><div class="trackInfo" title="' + extra + item + .title + '"><span class="trackTitle">' + extra + item + .title + '</span></div>'; + if (i == this.currentItem) { + activeItem = item; + activeItem.seq = i; + li.addClass('activeItem'); + } + if (i == (this.currentItem + 1)) { + $('#nextTrack').html(code).show(); + } + li.bind('click', jQuery.proxy(this.playPlaylistItem, this)); + ul.append(li.attr('seq', i).html(code)); + }, this)); + if (data.result.limits.total > 1) { + $('#nextText').show(); + if (activeItem && data.result.limits.total == activeItem.seq) { + $('#nextTrack').html( + '<div class="trackInfo">Last track in playlist</div>') + .show(); + } + $('#nowPlayingPlaylist').html('').append(ul); + } else { + $('#nextText').hide(); + $('#nowPlayingPlaylist').hide(); + $('#nextTrack').hide(); + } + if (!this.comparePlaylistItems(activeItem, this.activePlaylistItem)) { + this.activePlaylistItem = activeItem; + if (!this.updateActiveItemDurationRunOnce) { + this.updateActiveItemDurationRunOnce = true; + this.updatePlayer(); + } + } else if (!activeItem) { + this.stopRefreshTime(); + } + this.activePlaylist = data.result.items; + $('#videoDescription').show(); + $('#audioDescription').hide(); + $('#nowPlayingPanel').show(); + } + } else { + xbmc.rpc.request({ + 'context': this, + 'method': 'Player.GetItem', + 'params': { + 'playerid': this.activePlayerId, + 'properties': [ + 'title', + 'season', + 'episode', + 'plot', + 'runtime', + 'showtitle', + 'thumbnail' + ] + }, + 'success': function (data) { + if (data && data.result && data.result.item) { + this.activePlaylistItem = data.result.item; + if (!this.updateActiveItemDurationRunOnce) { + this.updateActiveItemDurationRunOnce = true; + this.updatePlayer(); + } + + $('#nextText').hide(); + $('#nowPlayingPlaylist').hide(); + $('#nextTrack').hide(); + + $('#videoDescription').show(); + $('#audioDescription').hide(); + $('#nowPlayingPanel').show(); + } else { + this.activePlaylist = null; + $('#videoDescription').hide(); + $('#nowPlayingPanel').hide(); + } + }, + 'error': function (data) { + xbmc.core.displayCommunicationError(); + if (this.autoRefreshVideoPlaylist) { + setTimeout(jQuery.proxy(this.updateVideoPlaylist, this), + 2000); /* Slow down request period */ + } + } + }); + } + if (this.autoRefreshVideoPlaylist) { + setTimeout(jQuery.proxy(this.updateVideoPlaylist, this), 1000); + } + }, + 'error': function (data) { + xbmc.core.displayCommunicationError(); + if (this.autoRefreshVideoPlaylist) { + setTimeout(jQuery.proxy(this.updateVideoPlaylist, this), + 2000); /* Slow down request period */ + } + } + }); + } +}
\ No newline at end of file diff --git a/debian/webinterface-default/js/json2.js b/debian/webinterface-default/js/json2.js new file mode 100644 index 0000000..edb7751 --- /dev/null +++ b/debian/webinterface-default/js/json2.js @@ -0,0 +1,492 @@ +/* + json2.js + 2012-10-08 + + Public Domain. + + NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. + + See http://www.JSON.org/js.html + + + This code should be minified before deployment. + See http://javascript.crockford.com/jsmin.html + + USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO + NOT CONTROL. + + + This file creates a global JSON object containing two methods: stringify + and parse. + + JSON.stringify(value, replacer, space) + value any JavaScript value, usually an object or array. + + replacer an optional parameter that determines how object + values are stringified for objects. It can be a + function or an array of strings. + + space an optional parameter that specifies the indentation + of nested structures. If it is omitted, the text will + be packed without extra whitespace. If it is a number, + it will specify the number of spaces to indent at each + level. If it is a string (such as '\t' or ' '), + it contains the characters used to indent at each level. + + This method produces a JSON text from a JavaScript value. + + When an object value is found, if the object contains a toJSON + method, its toJSON method will be called and the result will be + stringified. A toJSON method does not serialize: it returns the + value represented by the name/value pair that should be serialized, + or undefined if nothing should be serialized. The toJSON method + will be passed the key associated with the value, and this will be + bound to the value + + For example, this would serialize Dates as ISO strings. + + Date.prototype.toJSON = function (key) { + function f(n) { + // Format integers to have at least two digits. + return n < 10 ? '0' + n : n; + } + + return this.getUTCFullYear() + '-' + + f(this.getUTCMonth() + 1) + '-' + + f(this.getUTCDate()) + 'T' + + f(this.getUTCHours()) + ':' + + f(this.getUTCMinutes()) + ':' + + f(this.getUTCSeconds()) + 'Z'; + }; + + You can provide an optional replacer method. It will be passed the + key and value of each member, with this bound to the containing + object. The value that is returned from your method will be + serialized. If your method returns undefined, then the member will + be excluded from the serialization. + + If the replacer parameter is an array of strings, then it will be + used to select the members to be serialized. It filters the results + such that only members with keys listed in the replacer array are + stringified. + + Values that do not have JSON representations, such as undefined or + functions, will not be serialized. Such values in objects will be + dropped; in arrays they will be replaced with null. You can use + a replacer function to replace those with JSON values. + JSON.stringify(undefined) returns undefined. + + The optional space parameter produces a stringification of the + value that is filled with line breaks and indentation to make it + easier to read. + + If the space parameter is a non-empty string, then that string will + be used for indentation. If the space parameter is a number, then + the indentation will be that many spaces. + + Example: + + text = JSON.stringify(['e', {pluribus: 'unum'}]); + // text is '["e",{"pluribus":"unum"}]' + + + text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); + // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' + + text = JSON.stringify([new Date()], function (key, value) { + return this[key] instanceof Date ? + 'Date(' + this[key] + ')' : value; + }); + // text is '["Date(---current time---)"]' + + + JSON.parse(text, reviver) + This method parses a JSON text to produce an object or array. + It can throw a SyntaxError exception. + + The optional reviver parameter is a function that can filter and + transform the results. It receives each of the keys and values, + and its return value is used instead of the original value. + If it returns what it received, then the structure is not modified. + If it returns undefined then the member is deleted. + + Example: + + // Parse the text. Values that look like ISO date strings will + // be converted to Date objects. + + myData = JSON.parse(text, function (key, value) { + var a; + if (typeof value === 'string') { + a = +/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); + if (a) { + return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], + +a[5], +a[6])); + } + } + return value; + }); + + myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { + var d; + if (typeof value === 'string' && + value.slice(0, 5) === 'Date(' && + value.slice(-1) === ')') { + d = new Date(value.slice(5, -1)); + if (d) { + return d; + } + } + return value; + }); + + + This is a reference implementation. You are free to copy, modify, or + redistribute. +*/ + +/*jslint evil: true, regexp: true */ + +/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply, + call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, + getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, + lastIndex, length, parse, prototype, push, replace, slice, stringify, + test, toJSON, toString, valueOf +*/ + + +// Create a JSON object only if one does not already exist. We create the +// methods in a closure to avoid creating global variables. + +if (typeof JSON !== 'object') { + JSON = {}; +} + +(function () { + 'use strict'; + + function f(n) { + // Format integers to have at least two digits. + return n < 10 ? '0' + n : n; + } + + if (typeof Date.prototype.toJSON !== 'function') { + + Date.prototype.toJSON = function (key) { + + return isFinite(this.valueOf()) ? + this.getUTCFullYear() + '-' + + f(this.getUTCMonth() + 1) + '-' + + f(this.getUTCDate()) + 'T' + + f(this.getUTCHours()) + ':' + + f(this.getUTCMinutes()) + ':' + + f(this.getUTCSeconds()) + 'Z' : + null; + }; + + String.prototype.toJSON = + Number.prototype.toJSON = + Boolean.prototype.toJSON = function (key) { + return this.valueOf(); + }; + } + + var cx = + /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, + escapable = + /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, + gap, + indent, + meta = { // table of character substitutions + '\b': '\\b', + '\t': '\\t', + '\n': '\\n', + '\f': '\\f', + '\r': '\\r', + '"': '\\"', + '\\': '\\\\' + }, + rep; + + + function quote(string) { + + // If the string contains no control characters, no quote characters, and no + // backslash characters, then we can safely slap some quotes around it. + // Otherwise we must also replace the offending characters with safe escape + // sequences. + + escapable.lastIndex = 0; + return escapable.test(string) ? '"' + string.replace(escapable, function (a) { + var c = meta[a]; + return typeof c === 'string' ? + c : + '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + }) + '"' : '"' + string + '"'; + } + + + function str(key, holder) { + + // Produce a string from holder[key]. + + var i, // The loop counter. + k, // The member key. + v, // The member value. + length, + mind = gap, + partial, + value = holder[key]; + + // If the value has a toJSON method, call it to obtain a replacement value. + + if (value && typeof value === 'object' && + typeof value.toJSON === 'function') { + value = value.toJSON(key); + } + + // If we were called with a replacer function, then call the replacer to + // obtain a replacement value. + + if (typeof rep === 'function') { + value = rep.call(holder, key, value); + } + + // What happens next depends on the value's type. + + switch (typeof value) { + case 'string': + return quote(value); + + case 'number': + + // JSON numbers must be finite. Encode non-finite numbers as null. + + return isFinite(value) ? String(value) : 'null'; + + case 'boolean': + case 'null': + + // If the value is a boolean or null, convert it to a string. Note: + // typeof null does not produce 'null'. The case is included here in + // the remote chance that this gets fixed someday. + + return String(value); + + // If the type is 'object', we might be dealing with an object or an array or + // null. + + case 'object': + + // Due to a specification blunder in ECMAScript, typeof null is 'object', + // so watch out for that case. + + if (!value) { + return 'null'; + } + + // Make an array to hold the partial results of stringifying this object value. + + gap += indent; + partial = []; + + // Is the value an array? + + if (Object.prototype.toString.apply(value) === '[object Array]') { + + // The value is an array. Stringify every element. Use null as a placeholder + // for non-JSON values. + + length = value.length; + for (i = 0; i < length; i += 1) { + partial[i] = str(i, value) || 'null'; + } + + // Join all of the elements together, separated with commas, and wrap them in + // brackets. + + v = partial.length === 0 ? + '[]' : + gap ? + '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' : + '[' + partial.join(',') + ']'; + gap = mind; + return v; + } + + // If the replacer is an array, use it to select the members to be stringified. + + if (rep && typeof rep === 'object') { + length = rep.length; + for (i = 0; i < length; i += 1) { + if (typeof rep[i] === 'string') { + k = rep[i]; + v = str(k, value); + if (v) { + partial.push(quote(k) + (gap ? ': ' : ':') + v); + } + } + } + } else { + + // Otherwise, iterate through all of the keys in the object. + + for (k in value) { + if (Object.prototype.hasOwnProperty.call(value, k)) { + v = str(k, value); + if (v) { + partial.push(quote(k) + (gap ? ': ' : ':') + v); + } + } + } + } + + // Join all of the member texts together, separated with commas, + // and wrap them in braces. + + v = partial.length === 0 ? + '{}' : + gap ? + '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' : + '{' + partial.join(',') + '}'; + gap = mind; + return v; + } + } + + // If the JSON object does not yet have a stringify method, give it one. + + if (typeof JSON.stringify !== 'function') { + JSON.stringify = function (value, replacer, space) { + + // The stringify method takes a value and an optional replacer, and an optional + // space parameter, and returns a JSON text. The replacer can be a function + // that can replace values, or an array of strings that will select the keys. + // A default replacer method can be provided. Use of the space parameter can + // produce text that is more easily readable. + + var i; + gap = ''; + indent = ''; + + // If the space parameter is a number, make an indent string containing that + // many spaces. + + if (typeof space === 'number') { + for (i = 0; i < space; i += 1) { + indent += ' '; + } + + // If the space parameter is a string, it will be used as the indent string. + + } else if (typeof space === 'string') { + indent = space; + } + + // If there is a replacer, it must be a function or an array. + // Otherwise, throw an error. + + rep = replacer; + if (replacer && typeof replacer !== 'function' && + (typeof replacer !== 'object' || + typeof replacer.length !== 'number')) { + throw new Error('JSON.stringify'); + } + + // Make a fake root object containing our value under the key of ''. + // Return the result of stringifying the value. + + return str('', { + '': value + }); + }; + } + + + // If the JSON object does not yet have a parse method, give it one. + + if (typeof JSON.parse !== 'function') { + JSON.parse = function (text, reviver) { + + // The parse method takes a text and an optional reviver function, and returns + // a JavaScript value if the text is a valid JSON text. + + var j; + + function walk(holder, key) { + + // The walk method is used to recursively walk the resulting structure so + // that modifications can be made. + + var k, v, value = holder[key]; + if (value && typeof value === 'object') { + for (k in value) { + if (Object.prototype.hasOwnProperty.call(value, k)) { + v = walk(value, k); + if (v !== undefined) { + value[k] = v; + } else { + delete value[k]; + } + } + } + } + return reviver.call(holder, key, value); + } + + + // Parsing happens in four stages. In the first stage, we replace certain + // Unicode characters with escape sequences. JavaScript handles many characters + // incorrectly, either silently deleting them, or treating them as line endings. + + text = String(text); + cx.lastIndex = 0; + if (cx.test(text)) { + text = text.replace(cx, function (a) { + return '\\u' + + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + }); + } + + // In the second stage, we run the text against regular expressions that look + // for non-JSON patterns. We are especially concerned with '()' and 'new' + // because they can cause invocation, and '=' because it can cause mutation. + // But just to be safe, we want to reject all unexpected forms. + + // We split the second stage into 4 regexp operations in order to work around + // crippling inefficiencies in IE's and Safari's regexp engines. First we + // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we + // replace all simple value tokens with ']' characters. Third, we delete all + // open brackets that follow a colon or comma or that begin the text. Finally, + // we look to see that the remaining characters are only whitespace or ']' or + // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. + + if (/^[\],:{}\s]*$/ + .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') + .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']') + .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { + + // In the third stage we use the eval function to compile the text into a + // JavaScript structure. The '{' operator is subject to a syntactic ambiguity + // in JavaScript: it can begin a block or an object literal. We wrap the text + // in parens to eliminate the ambiguity. + + j = eval('(' + text + ')'); + + // In the optional fourth stage, we recursively walk the new structure, passing + // each name/value pair to a reviver function for possible transformation. + + return typeof reviver === 'function' ? + walk({ + '': j + }, '') : + j; + } + + // If the text is not JSON parseable, then a SyntaxError is thrown. + + throw new SyntaxError('JSON.parse'); + }; + } +}());
\ No newline at end of file diff --git a/debian/webinterface-default/js/xbmc.core.js b/debian/webinterface-default/js/xbmc.core.js new file mode 100644 index 0000000..2be141c --- /dev/null +++ b/debian/webinterface-default/js/xbmc.core.js @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/>. + * + */ + +(function (window) { + "use strict"; + + var xbmc = window.xbmc || {}; + xbmc.core = { + 'DEFAULT_ALBUM_COVER': 'images/DefaultAlbumCover.png', + 'DEFAULT_VIDEO_COVER': 'images/DefaultVideo.png', + 'JSON_RPC': 'jsonrpc', + 'applyDeviceFixes': function () { + window.document.addEventListener('touchmove', function (e) { + e.preventDefault(); + }); + }, + 'displayCommunicationError': function (m) { + window.clearTimeout(xbmc.core.commsErrorTimeout); + var message = m || 'Connection to server lost'; + $('#commsErrorPanel').html(message).show(); + xbmc.core.commsErrorTimeout = window.setTimeout('xbmc.core.hideCommunicationError()', 5000); + }, + 'durationToString': function (duration) { + var total_seconds = duration || 0, + seconds = total_seconds % 60, + minutes = Math.floor(total_seconds / 60) % 60, + hours = Math.floor(total_seconds / 3600), + result = ((hours > 0 && ((hours < 10 ? '0' : '') + hours + ':')) || ''); + result += (minutes < 10 ? '0' : '') + minutes + ':'; + result += (seconds < 10 ? '0' : '') + seconds; + return result; + }, + 'getCookie': function (name) { + var i, + match, + haystack = window.document.cookie.split(';'); + for (i = 0; i < haystack.length; i += 1) { + match = haystack[i].match(/^\s*[\S\s]*=([\s\S]*)\s*$/); + if (match && match.length === 2) { + return match[1]; + } + } + return null; + }, + 'hideCommunicationError': function () { + $('#commsErrorPanel').hide(); + }, + 'setCookie': function (name, value, days) { + var date, + expires; + if (name) { + if (days) { + date = new Date(); + date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); + expires = "; expires=" + date.toGMTString(); + } else { + expires = ''; + } + window.document.cookie = name + "=" + value + expires + "; path=/"; + } + }, + 'timeToDuration': function (time) { + var duration; + time = time || {}; + duration = ((time.hours || 0) * 3600); + duration += ((time.minutes || 0) * 60); + duration += (time.seconds || 0); + return duration; + } + }; + + window.xbmc = xbmc; +}(window));
\ No newline at end of file diff --git a/debian/webinterface-default/js/xbmc.init.js b/debian/webinterface-default/js/xbmc.init.js new file mode 100644 index 0000000..c61a81f --- /dev/null +++ b/debian/webinterface-default/js/xbmc.init.js @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/>. + * + */ + +(function () { + "use strict" + + var mediaLibrary = new MediaLibrary(), + nowPlayingManager = new NowPlayingManager(); + xbmc.core.applyDeviceFixes(); +}());
\ No newline at end of file diff --git a/debian/webinterface-default/js/xbmc.launcher.js b/debian/webinterface-default/js/xbmc.launcher.js new file mode 100644 index 0000000..4f039d6 --- /dev/null +++ b/debian/webinterface-default/js/xbmc.launcher.js @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/>. + * + */ + +(function (document) { + "use strict"; + + var i, + script, + debug = false, + /* Set to true to disable cached javascript */ + version = (debug ? Math.random() : '2.1.0'), + scripts = [ + "js/jquery-1.8.2.min.js", + "js/json2.js", + "js/iscroll-min.js", + "js/xbmc.core.js", + "js/xbmc.rpc.js", + "js/MediaLibrary.js", + "js/NowPlayingManager.js", + "js/xbmc.init.js" + ]; + + for (i = 0; i < scripts.length; i += 1) { + script = '<script type="text/javascript" src="'; + script += scripts[i] + '?' + version; + script += '"><\/script>'; + document.write(script); + } +}(window.document));
\ No newline at end of file diff --git a/debian/webinterface-default/js/xbmc.rpc.js b/debian/webinterface-default/js/xbmc.rpc.js new file mode 100644 index 0000000..60b1d8b --- /dev/null +++ b/debian/webinterface-default/js/xbmc.rpc.js @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/>. + * + */ + +(function (window) { + + var xbmc = window.xbmc || {}; + + xbmc.rpc = { + 'default_options': { + 'contentType': 'application/json', + 'dataType': 'json', + 'type': 'POST', + 'success': function () { + $('#spinner').hide(); + } + }, + 'request': function (options) { + var request_options = jQuery.extend({}, this.default_options, options); + request_options.url = xbmc.core.JSON_RPC + '?' + options.method; + request_options.data = JSON.stringify({ + 'jsonrpc': '2.0', + 'method': options.method, + 'id': 1, + 'params': request_options.params + }); + return jQuery.ajax(request_options) + } + }; + + window.xbmc = xbmc; + +}(window));
\ No newline at end of file |