summaryrefslogtreecommitdiffstats
path: root/debian/webinterface-default/js
diff options
context:
space:
mode:
Diffstat (limited to 'debian/webinterface-default/js')
-rwxr-xr-xdebian/webinterface-default/js/MediaLibrary.js1420
-rwxr-xr-xdebian/webinterface-default/js/NowPlayingManager.js661
-rw-r--r--debian/webinterface-default/js/json2.js492
-rw-r--r--debian/webinterface-default/js/xbmc.core.js90
-rw-r--r--debian/webinterface-default/js/xbmc.init.js27
-rw-r--r--debian/webinterface-default/js/xbmc.launcher.js46
-rw-r--r--debian/webinterface-default/js/xbmc.rpc.js49
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">&nbsp;</td><td class="nextAlbum">&nbsp;</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>&nbsp;</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('&nbsp').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('&nbsp'));
+ }
+ $('#albumDetails' + event.data.album.albumid + ' .resultSet').append(
+ trackRow);
+
+ var trackRow2 = $('<tr>').addClass('fillerTrackRow2');
+ trackRow2.append($('<td>').addClass('albumBG').html('&nbsp'));
+ for (i = 0; i < 5; i++) {
+ trackRow2.append($('<td>').html('&nbsp'));
+ }
+ $('#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&deg;</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 || '&nbsp;');
+ 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 '&nbsp;'),
+ 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