diff --git a/static/js/pandora/home.0xdb.js b/static/js/pandora/home.0xdb.js new file mode 100644 index 00000000..7f5506cb --- /dev/null +++ b/static/js/pandora/home.0xdb.js @@ -0,0 +1,284 @@ +// vim: et:ts=4:sw=4:sts=4:ft=javascript + +'use strict'; + +pandora.ui.home = function() { + + var that = $('
') + .addClass('OxScreen') + .css({ + position: 'absolute', + width: '100%', + height: '100%', + opacity: 0, + zIndex: 1001 + }), + $reflectionImage = $('') + .attr({src: '/static/png/logo.png'}) + .css({ + position: 'absolute', + left: 0, + top: '160px', + right: 0, + bottom: 0, + width: '320px', + margin: 'auto', + opacity: 0, + MozTransform: 'scaleY(-1)', + OTransform: 'scaleY(-1)', + WebkitTransform: 'scaleY(-1)' + }) + .appendTo(that), + $reflectionGradient = $('
') + .addClass('OxReflection') + .css({ + position: 'absolute', + left: 0, + top: '160px', + right: 0, + bottom: 0, + width: '320px', + height: '160px', + margin: 'auto', + }) + .appendTo(that), + $logo = $('') + .attr({ + id: 'logo', + src: '/static/png/logo.png' + }) + .css({ + position: 'absolute', + left: 0, + top: 0, + right: 0, + bottom: '160px', + width: window.innerWidth + 'px', + margin: 'auto', + cursor: 'pointer' + }) + .bind({ + click: function() { + $browseButton.triggerEvent('click'); + } + }) + .appendTo(that), + $findInput = Ox.Input({ + width: 156 + }) + .css({ + position: 'absolute', + left: 0, + top: '48px', + right: '164px', + bottom: 0, + margin: 'auto', + opacity: 0 + }) + .click(function(e) { + // fixme: why? + e.stopPropagation(); + }) + .bindEvent({ + submit: function(data) { + if (data.value) { + $findButton.triggerEvent('click'); + } else { + $browseButton.triggerEvent('click'); + } + } + }) + .appendTo(that), + $findButton = Ox.Button({ + title: 'Find', + width: 74 + }) + .css({ + position: 'absolute', + left: '82px', + top: '48px', + right: 0, + bottom: 0, + margin: 'auto', + opacity: 0 + }) + .bindEvent({ + click: function() { + var folder = pandora.getListData().folder, + value = $findInput.value(); + folder && pandora.$ui.folderList[folder].options({selected: []}); + pandora.$ui.findSelect.value('*'); + pandora.$ui.findInput.value(value); + that.fadeOutScreen(); + pandora.UI.set({ + page: '', + find: { + conditions: value === '' + ? [] + : [{key: '*', value: value, operator: '='}], + operator: '&' + } + }); + } + }) + .appendTo(that), + $browseButton = Ox.Button({ + title: 'Browse', + width: 74 + }) + .css({ + position: 'absolute', + left: '246px', + top: '48px', + right: 0, + bottom: 0, + margin: 'auto', + opacity: 0 + }) + .bindEvent({ + click: function() { + pandora.UI.set({ + page: pandora.user.ui.page == 'home' ? '' : pandora.user.ui.page + }); + that.fadeOutScreen(); + } + }) + .appendTo(that), + $signupButton = Ox.Button({ + title: 'Sign Up', + width: 74 + }) + .css({ + position: 'absolute', + left: 0, + top: '112px', + right: '246px', + bottom: 0, + margin: 'auto', + opacity: 0 + }) + .bindEvent({ + click: function() { + pandora.UI.set({page: 'signup'}); + that.fadeOutScreen(); + } + }), + $signinButton = Ox.Button({ + title: 'Sign In', + width: 74 + }) + .css({ + position: 'absolute', + left: 0, + top: '112px', + right: '82px', + bottom: 0, + margin: 'auto', + opacity: 0 + }) + .bindEvent({ + click: function() { + pandora.UI.set({page :'signin'}); + that.fadeOutScreen(); + } + }), + $preferencesButton = Ox.Button({ + title: 'Preferences', + width: 156 + }) + .css({ + position: 'absolute', + left: 0, + top: '112px', + right: '164px', + bottom: 0, + margin: 'auto', + opacity: 0 + }) + .bindEvent({ + click: function() { + pandora.UI.set({page: 'preferences'}); + that.fadeOutScreen(); + } + }), + $aboutButton = Ox.Button({ + title: 'About ' + pandora.site.site.name, + width: 156 + }) + .css({ + position: 'absolute', + left: '164px', + top: '112px', + right: 0, + bottom: 0, + margin: 'auto', + opacity: 0 + }) + .bindEvent({ + click: function() { + pandora.UI.set({page: 'about'}); + that.fadeOutScreen(); + } + }) + .appendTo(that), + $text = $('
') + .html('A Movie Database. \u2620 2007-2012 0x2620. All Open Source.') + .css({ + position: 'absolute', + left: 0, + top: '176px', + right: 0, + bottom: 0, + width: '360px', + height: '16px', + margin: 'auto', + opacity: 0, + textAlign: 'center' + }) + .appendTo(that); + + if (pandora.user.level == 'guest') { + $signupButton.appendTo(that); + $signinButton.appendTo(that); + } else { + $preferencesButton.appendTo(that); + } + + that.fadeInScreen = function() { + that.appendTo(Ox.UI.$body).animate({opacity: 1}, 500, function() { + that.find(':not(#logo)').animate({opacity: 1}, 250, function() { + $findInput.focusInput(true); + }); + }); + $logo.animate({width: '320px'}, 500); + return that; + }; + + that.fadeOutScreen = function() { + that.find(':not(#logo)').hide(); + $logo.animate({width: window.innerWidth + 'px'}, 500); + that.animate({opacity: 0}, 500, function() { + that.remove(); + }); + pandora.$ui.tv && pandora.$ui.tv.unmute(); + return that; + }; + + that.hideScreen = function() { + that.hide().remove(); + that.find(':not(#logo)').css({opacity: 0}); + $logo.css({width: window.innerWidth + 'px'}); + return that; + }; + + that.showScreen = function() { + $logo.css({width: '320px'}); + that.find(':not(#logo)').css({opacity: 1}); + that.css({opacity: 1}).appendTo(Ox.UI.$body); + $findInput.focusInput(true); + return that; + }; + + return that; + +}; diff --git a/static/js/pandora/home.js b/static/js/pandora/home.js index 7f5506cb..4a124f99 100644 --- a/static/js/pandora/home.js +++ b/static/js/pandora/home.js @@ -222,7 +222,7 @@ pandora.ui.home = function() { }) .appendTo(that), $text = $('
') - .html('A Movie Database. \u2620 2007-2012 0x2620. All Open Source.') + .html('pan.do/ra. \u2620 2007-2012 0x2620. All Open Source.') .css({ position: 'absolute', left: 0, diff --git a/static/js/pandora/infoView.0xdb.js b/static/js/pandora/infoView.0xdb.js new file mode 100644 index 00000000..84294772 --- /dev/null +++ b/static/js/pandora/infoView.0xdb.js @@ -0,0 +1,886 @@ +'use strict'; + +pandora.ui.infoView = function(data) { + + // fixme: given that currently, the info view doesn't scroll into view nicely + // when collapsing the movies browser, the info view should become a split panel + + var ui = pandora.user.ui, + canEdit = pandora.site.capabilities.canEditMetadata[pandora.user.level], + css = { + marginTop: '4px', + textAlign: 'justify', + MozUserSelect: 'text', + WebkitUserSelect: 'text' + }, + iconRatio = ui.icons == 'posters' + ? (ui.showSitePosters ? 5/8 : data.posterRatio) : 1, + iconSize = ui.infoIconSize, + iconWidth = iconRatio > 1 ? iconSize : Math.round(iconSize * iconRatio), + iconHeight = iconRatio < 1 ? iconSize : Math.round(iconSize / iconRatio), + iconLeft = iconSize == 256 ? Math.floor((iconSize - iconWidth) / 2) : 0, + borderRadius = ui.icons == 'posters' ? 0 : iconSize / 8, + isEditable = canEdit && data.id.substr(0, 2) == '0x', + listWidth = 144 + Ox.UI.SCROLLBAR_SIZE, + margin = 16, + statisticsWidth = 128, + uid = Ox.uid(), + + that = Ox.Element(), + + $list, + + $info = $('
') + .css({ + position: 'absolute', + left: canEdit && !ui.showIconBrowser ? -listWidth + 'px' : 0, + top: 0, + right: 0, + }) + .appendTo(that.$element), + + $data = Ox.Container() + .css({ + position: 'absolute', + left: (canEdit ? listWidth : 0) + 'px', + top: 0, + right: 0, + height: pandora.$ui.contentPanel.size(1) + 'px' + }) + .appendTo($info), + + $icon = Ox.Element({ + element: '', + tooltip: canEdit ? ( + !ui.showIconBrowser + ? 'Doubleclick to edit icon' + : 'Doubleclick to hide icons' + ) : '' + }) + .attr({ + src: '/' + data.id + '/' + ( + ui.icons == 'posters' + ? (ui.showSitePosters ? 'siteposter' : 'poster') : 'icon' + ) + '512.jpg?' + uid + }) + .css({ + position: 'absolute', + left: margin + iconLeft + 'px', + top: margin + 'px', + width: iconWidth + 'px', + height: iconHeight + 'px', + borderRadius: borderRadius + 'px', + cursor: 'pointer' + }) + .bindEvent({ + singleclick: toggleIconSize + }) + .appendTo($data.$element), + + $reflection = $('
') + .addClass('OxReflection') + .css({ + position: 'absolute', + left: margin + 'px', + top: margin + iconHeight + 'px', + width: iconSize + 'px', + height: iconSize / 2 + 'px', + overflow: 'hidden' + }) + .appendTo($data.$element), + + $reflectionIcon = $('') + .attr({ + src: '/' + data.id + '/' + ( + ui.icons == 'posters' + ? (ui.showSitePosters ? 'siteposter' : 'poster') : 'icon' + ) + '512.jpg?' + uid + }) + .css({ + position: 'absolute', + left: iconLeft + 'px', + bottom: 0, + width: iconWidth + 'px', + height: iconHeight + 'px', + borderRadius: borderRadius + 'px' + }) + .appendTo($reflection), + + $reflectionGradient = $('
') + .css({ + position: 'absolute', + width: iconSize + 'px', + height: iconSize / 2 + 'px' + }) + .appendTo($reflection), + + $text = Ox.Element({ + tooltip: canEdit && !isEditable ? 'Doubleclick to reload metadata' : '' + }) + .css({ + position: 'absolute', + left: margin + (iconSize == 256 ? 256 : iconWidth) + margin + 'px', + top: margin + 'px', + right: margin + statisticsWidth + margin + 'px', + }) + .bindEvent(canEdit && !isEditable ? { + doubleclick: reloadMetadata + } : {}) + .appendTo($data.$element), + + $statistics = $('
') + .css({ + position: 'absolute', + width: statisticsWidth + 'px', + top: margin + 'px', + right: margin + 'px' + }) + .appendTo($data.$element), + + $capabilities, + + $browserImages = []; + + pandora.createLinks($text); // FIXME: this is wrong for editables that already have clickLink + + // Title ------------------------------------------------------------------- + + $('
') + .css({ + marginTop: '-2px' + }) + .append( + Ox.Editable({ + editable: isEditable, + format: function(value) { + return formatTitle(value); + }, + tooltip: isEditable ? 'Doubleclick to edit' : '', + value: data.title + }) + .css({ + display: 'inline-block', + marginBottom: '-3px', + fontWeight: 'bold', + fontSize: '13px', + MozUserSelect: 'text', + WebkitUserSelect: 'text' + }) + .bindEvent({ + submit: function(event) { + editMetadata('title', event.value); + } + }) + .appendTo($text) + ) + .appendTo($text); + + // Director ---------------------------------------------------------------- + + if (data.director || isEditable) { + $('
') + .css({ + marginTop: '2px' + }) + .append( + Ox.Editable({ + clickLink: pandora.clickLink, + editable: isEditable, + format: function(value) { + return formatValue(value.split(', '), 'name'); + }, + placeholder: formatLight('Unknown Director'), + tooltip: isEditable ? 'Doubleclick to edit' : '', + value: data.director ? data.director.join(', ') : 'Unknown Director' + }) + .css({ + display: 'inline-block', + marginBottom: '-3px', + fontWeight: 'bold', + fontSize: '13px', + MozUserSelect: 'text', + WebkitUserSelect: 'text' + }) + .bindEvent({ + submit: function(event) { + editMetadata('director', event.value); + } + }) + ) + .appendTo($text); + } + + // Country, Year, Language, Runtime ---------------------------------------- + + if (isEditable) { + var $div = $('
') + .css(css) + .appendTo($text); + ['country', 'year'].forEach(function(key, i) { + i && $('
').css({float: 'left'}).html('; ').appendTo($div); + $('
') + .css({float: 'left'}) + .html(formatKey(key).replace('', ' ')) + .appendTo($div); + Ox.Editable({ + clickLink: pandora.clickLink, + format: function(value) { + return formatValue(value.split(', '), key) + }, + placeholder: formatLight('unknown'), + tooltip: 'Doubleclick to edit', + value: key == 'country' + ? (data[key] ? data[key].join(', ') : ['']) + : data[key] || '' + }) + .css({float: 'left'}) + .bindEvent({ + submit: function(event) { + editMetadata(key, event.value); + } + }) + .appendTo($div); + }); + } else if (data.country || data.year || data.language || data.runtime) { + var html = []; + ['country', 'year', 'language', 'runtime'].forEach(function(key) { + if (data[key]) { + html.push( + formatKey(key) + ( + key != 'runtime' ? formatValue(data[key], key) + : data[key] < 60 ? Math.round(data[key]) + ' sec' + : Math.round(data[key] / 60) + ' min' + ) + ) + } + }); + $('
').css(css).html(html.join('; ')).appendTo($text); + } + + // Alternative Titles ------------------------------------------------------ + + data.alternativeTitles && $('
') + .css(css) + .html( + formatKey('Alternative Title' + (data.alternativeTitles.length == 1 ? '' : 's')) + + data.alternativeTitles.map(function(value) { + return value[0] + (value[1] ? ' ' + + formatLight('(' + value[1] + ')') : ''); + }).join(', ') + ) + .appendTo($text); + + // fixme: episodeDirector seems to be always missing + if (data.episodeDirector || data.writer || data.producer || data.cinematographer || data.editor) { + $div = $('
') + .css(css) + .appendTo($text); + html = []; + ['episodeDirector', 'writer', 'producer', 'cinematographer', 'editor'].forEach(function(key) { + data[key] && html.push( + formatKey(key == 'episodeDirector' ? 'director' : key) + formatValue(data[key], 'name') + ); + }); + $div.html(html.join('; ')); + } + + data.cast && $('
') + .css(css) + .html( + formatKey('cast') + data.cast.map(function(value) { + value.character = value.character.replace('(uncredited)', '').trim(); + return formatValue(value.actor, 'name') + + (value.character ? ' ' + + formatLight('(' + formatValue(value.character) + ')') + : ''); + }).join(', ') + ) + .appendTo($text); + + if (data.genre || data.keyword) { + $div = $('
') + .css(css) + .appendTo($text); + html = []; + ['genre', 'keyword'].forEach(function(key) { + data[key] && html.push( + formatKey(key == 'keyword' ? 'keywords' : key) + + formatValue(data[key], key) + ); + }); + $div.html(html.join('; ')); + } + + data.summary && $('
') + .css(css) + .html( + formatKey('summary') + data.summary + ) + .appendTo($text); + + data.trivia && data.trivia.forEach(function(value) { + $('
') + .css({ + display: 'table-row' + }) + .append( + $('
') + .css({ + display: 'table-cell', + width: '12px', + paddingTop: '4px' + }) + .html('') + ) + .append( + $('
') + .css({ + display: 'table-cell', + paddingTop: '4px', + textAlign: 'justify', + MozUserSelect: 'text', + WebkitUserSelect: 'text' + }) + .html(value) + ) + .append( + $('
').css({clear: 'both'}) + ) + .appendTo($text); + }); + + data.filmingLocations && $('
') + .css(css) + .html( + formatKey('Filming Locations') + data.filmingLocations.map(function(location) { + return '' + location + '' + }).join(', ') + ) + .appendTo($text); + + data.releasedate && $('
') + .css(css) + .html( + formatKey('Release Date') + Ox.formatDate(data.releasedate, '%A, %B %e, %Y') + ) + .appendTo($text); + + if (data.budget || data.gross || data.profit) { + $div = $('
') + .css(css) + .appendTo($text); + html = []; + ['budget', 'gross', 'profit'].forEach(function(key) { + data[key] && html.push( + formatKey(key) + Ox.formatCurrency(data[key], '$') + ); + }); + $div.html(html.join('; ')); + } + + if (data.rating || data.votes) { + $div = $('
') + .css(css) + .appendTo($text); + html = []; + ['rating', 'votes'].forEach(function(key) { + data[key] && html.push( + formatKey(key) + Ox.formatNumber(data[key], key == 'rating' ? 0 : 2) + '%' + ); + }); + $div.html(html.join('; ')); + } + + if (data.connections) { + $div = $('
') + .css(css) + .appendTo($text); + html = []; + [ + 'Edited from', 'Edited into', + 'Features', 'Featured in', + 'Follows', 'Followed by', + 'References', 'Referenced in', + 'Remake of', 'Remade as', + 'Spin off from', 'Spin off', + 'Spoofs', 'Spoofed in' + ].forEach(function(key) { + data.connections[key] && html.push( + formatKey(key) + data.connections[key].map(function(connection) { + return connection.item + ? '' + connection.title + '' + : connection.title; + }).join(', ') + ); + }); + $div.html(html.join('; ')); + } + + ['reviews', 'links'].forEach(function(key) { + data[key] && $('
') + .css(css) + .html( + formatKey(key) + data[key].map(function(value) { + return '' + value.source + '' + }).join(', ') + ) + .appendTo($text); + }); + + $('
').css({height: '16px'}).appendTo($text); + + // Hue, Saturation, Lightness, Volume -------------------------------------- + + ['hue', 'saturation', 'lightness', 'volume'].forEach(function(key) { + $('
') + .css({marginBottom: '4px'}) + .append(formatKey(key, true)) + .append( + Ox.Theme.formatColor( + data[key] || 0, key == 'volume' ? 'lightness' : key + ).css({textAlign: 'right'}) + ) + .appendTo($statistics); + }); + + // Rights Level ------------------------------------------------------------ + + var $rightsLevel = $('
'); + $('
') + .css({marginBottom: '4px'}) + .append(formatKey('Rights Level', true)) + .append($rightsLevel) + .appendTo($statistics); + renderRightsLevel(); + + // Notes ------------------------------------------------------------------- + + if (canEdit) { + + $('
') + .css({marginBottom: '4px'}) + .append(formatKey('Notes', true)) + .append( + Ox.Editable({ + height: 128, + placeholder: formatLight('No notes'), + tooltip: 'Doubleclick to edit', + type: 'textarea', + value: data.notes, + width: 128 + }) + .bindEvent({ + submit: function(event) { + pandora.api.edit({ + id: data.id, + notes: event.value + }, function(result) { + // ... + }); + } + }) + ) + .appendTo($statistics); + + } + + $('
').css({height: '16px'}).appendTo($statistics); + + if (canEdit) { + $icon.bindEvent({ + doubleclick: function() { + pandora.UI.set('showIconBrowser', !ui.showIconBrowser); + $info.animate({ + left: ui.showIconBrowser ? 0 : -listWidth + 'px' + }, 250); + $icon.options({ + tooltip: !pandora.user.ui.showIconBrowser + ? 'Doubleclick to edit icon' + : 'Doubleclick to hide icons' + }); + } + }); + renderList(); + } + + function editMetadata(key, value) { + if (value != data[key]) { + var edit = {id: data.id}; + if (key == 'title') { + Ox.extend(edit, parseTitle(value)); + } else if(['director', 'country'].indexOf(key) > -1) { + edit[key] = value ? value.split(', ') : []; + } else { + edit[key] = value; + } + pandora.api.edit(edit, function(result) { + if (result.data.id != data.id) { + Ox.Request.clearCache(); // fixme: too much + pandora.UI.set({item: result.data.id}); + pandora.$ui.browser.value(data.id, 'id', result.data.id); + } + // FIXME: value function should accept {k: v, ...} + pandora.$ui.browser.value(result.data.id, 'title', result.data.title); + pandora.$ui.browser.value(result.data.id, 'director', result.data.director); + pandora.$ui.browser.value(result.data.id, 'country', result.data.country); + pandora.$ui.browser.value(result.data.id, 'year', result.data.year); + //pandora.$ui.contentPanel.replaceElement(0, pandora.$ui.browser = pandora.ui.browser()); + }); + } + } + + function formatKey(key, isStatistics) { + return isStatistics + ? $('
').css({marginBottom: '4px', fontWeight: 'bold'}).html(Ox.toTitleCase(key)) + : '' + Ox.toTitleCase(key) + ': '; + } + + function formatLight(str) { + return '' + str + ''; + } + + function formatTitle(title) { + var match = /(\(S\d{2}E\d{2}\))/.exec(title); + if (match) { + title = title.replace(match[0], formatLight(match[0])); + } + return title + ( + data.originalTitle && data.originalTitle != title + ? ' ' + formatLight('(' + data.originalTitle + ')') : '' + ); + } + + function formatValue(value, key) { + return (Ox.isArray(value) ? value : [value]).map(function(value) { + return key ? + '' + value + '' + : value; + }).join(', '); + } + + function getRightsLevelElement(rightsLevel) { + return Ox.Theme.formatColorLevel( + rightsLevel, + pandora.site.rightsLevels.map(function(rightsLevel) { + return rightsLevel.name; + }) + ); + } + + function parseTitle(title) { + var data = {title: title}, + match = /(\(S(\d{2})E(\d{2})\))/.exec(title), + split; + if (match) { + data.season = parseInt(match[2], 10); + data.episode = parseInt(match[3], 10); + split = title.split(match[1]); + data.seriesTitle = split[0].trim(); + data.episodeTitle = split[1].trim(); + } + return data; + } + + function reloadMetadata() { + var item = ui.item; + // fixme: maybe there's a better method name for this? + pandora.api.updateExternalData({ + id: ui.item + }, function(result) { + //reloading metadata might change results too(i.e. genre) + Ox.Request.clearCache(); + if (ui.item == item && ui.itemView == 'info') { + pandora.$ui.contentPanel.replaceElement( + 1, pandora.$ui.item = pandora.ui.item() + ); + } + }); + } + + function renderCapabilities(rightsLevel) { + var capabilities = Ox.merge( + canEdit ? [{name: 'canSeeItem', symbol: 'Find'}] : [], + [ + {name: 'canPlayClips', symbol: 'PlayInToOut'}, + {name: 'canPlayVideo', symbol: 'Play'}, + {name: 'canDownloadVideo', symbol: 'Download'} + ] + ), + userLevels = canEdit ? pandora.site.userLevels : [pandora.user.level]; + $capabilities.empty(); + userLevels.forEach(function(userLevel, i) { + var $element, + $line = $('
') + .css({ + height: '16px', + marginBottom: '4px' + }) + .appendTo($capabilities); + if (canEdit) { + $element = Ox.Theme.formatColorLevel(i, userLevels.map(function(userLevel) { + return Ox.toTitleCase(userLevel); + }), [0, 240]); + Ox.Label({ + textAlign: 'center', + title: Ox.toTitleCase(userLevel), + width: 60 + }) + .addClass('OxColor OxColorGradient') + .css({ + float: 'left', + height: '12px', + paddingTop: '2px', + background: $element.css('background'), + fontSize: '8px', + color: $element.css('color') + }) + .data({OxColor: $element.data('OxColor')}) + .appendTo($line); + } + capabilities.forEach(function(capability) { + var hasCapability = pandora.site.capabilities[capability.name][userLevel] >= rightsLevel, + $element = Ox.Theme.formatColorLevel(hasCapability, ['', '']); + Ox.Button({ + tooltip: (canEdit ? Ox.toTitleCase(userLevel) : 'You') + ' ' + + (hasCapability ? 'can' : 'can\'t') + ' ' + + Ox.map(Ox.toSlashes(capability.name).split('/'), function(word, i) { + return i == 0 ? null : word.toLowerCase(); + }).join(' ') + .replace('see item', 'see the item') + .replace('play video', 'play the full video') + .replace('download video', 'download the video'), + title: capability.symbol, + type: 'image' + }) + .addClass('OxColor OxColorGradient') + .css({background: $element.css('background')}) + .css('margin' + (canEdit ? 'Left' : 'Right'), '4px') + .data({OxColor: $element.data('OxColor')}) + .appendTo($line); + }); + if (!canEdit) { + Ox.Button({ + title: 'Help', + tooltip: 'About Rights', + type: 'image' + }) + .css({marginLeft: '52px'}) + .bindEvent({ + click: function() { + pandora.UI.set({page: 'rights'}); + } + }) + .appendTo($line); + } + }); + } + + function renderList() { + pandora.api.get({ + id: data.id, + keys: [ui.icons == 'posters' ? 'posters' : 'frames'] + }, 0, function(result) { + var images = result.data[ui.icons == 'posters' ? 'posters' : 'frames'], + selectedImage = images.filter(function(image) { + return image.selected; + })[0]; + $list = Ox.IconList({ + defaultRatio: ui.icons == 'posters' ? 5/8 : data.stream.aspectratio, + fixedRatio: ui.icons == 'posters' ? false : data.stream.aspectratio, + item: function(data, sort, size) { + var ratio = data.width / data.height; + size = size || 128; + return { + height: ratio <= 1 ? size : size / ratio, + id: data['id'], + info: data.width + ' x ' + data.height + ' px', + title: ui.icons == 'posters' ? data.source : Ox.formatDuration(data.position), + url: data.url.replace('http://', '//'), + width: ratio >= 1 ? size : size * ratio + } + }, + items: images, + keys: ui.icons == 'posters' + ? ['index', 'source', 'width', 'height', 'url'] + : ['index', 'position', 'width', 'height', 'url'], + max: 1, + min: 1, + orientation: 'both', + // fixme: should never be undefined + selected: selectedImage ? [selectedImage['index']] : [], + size: 128, + sort: [{key: 'index', operator: '+'}], + unique: 'index' + }) + .css({ + display: 'block', + position: 'absolute', + left: 0, + top: 0, + width: listWidth + 'px', + height: pandora.$ui.contentPanel.size(1) + 'px' + }) + .bindEvent({ + select: function(event) { + var index = event.ids[0]; + selectedImage = images.filter(function(image) { + return image.index == index; + })[0]; + var imageRatio = selectedImage.width / selectedImage.height, + src = selectedImage.url.replace('http://', '//'); + if ($browserImages.length == 0) { + $browserImages = pandora.$ui.browser.find('img[src*="/' + data.id + '/"]'); + } + if (ui.icons == 'posters' && !ui.showSitePosters) { + $browserImages.each(function() { + var $this = $(this), + size = Math.max($this.width(), $this.height()); + $this.attr({src: src}); + ui.icons == 'posters' && $this.css(imageRatio < 1 ? { + width: Math.round(size * imageRatio) + 'px', + height: size + 'px' + } : { + width: size + 'px', + height: Math.round(size / imageRatio) + 'px' + }); + }); + $icon.attr({src: src}); + $reflectionIcon.attr({src: src}); + iconRatio = imageRatio; + iconSize = iconSize == 256 ? 512 : 256; + toggleIconSize(); + } + pandora.api[ui.icons == 'posters' ? 'setPoster' : 'setPosterFrame'](Ox.extend({ + id: data.id + }, ui.icons == 'posters' ? { + source: selectedImage.source + } : { + // fixme: api slightly inconsistent, this shouldn't be "position" + position: selectedImage.index + }), function() { + // fixme: update the info (video preview) frame as well + var src; + if (ui.icons == 'frames') { + src = '/' + data.id + '/icon512.jpg?' + Ox.uid() + $icon.attr({src: src}); + $reflectionIcon.attr({src: src}); + } + $browserImages.each(function() { + $(this).attr({src: '/' + data.id + '/' + ( + ui.icons == 'posters' ? 'poster' : 'icon' + ) + '128.jpg?' + Ox.uid()}); + }); + }); + } + }) + .appendTo($info); + $list.size(); + }); + } + + function renderRightsLevel() { + var $rightsLevelElement = getRightsLevelElement(data.rightslevel), + $rightsLevelSelect; + $rightsLevel.empty(); + if (canEdit) { + $rightsLevelSelect = Ox.Select({ + items: pandora.site.rightsLevels.map(function(rightsLevel, i) { + return {id: i, title: rightsLevel.name}; + }), + width: 128, + value: data.rightslevel + }) + .addClass('OxColor OxColorGradient') + .css({ + marginBottom: '4px', + background: $rightsLevelElement.css('background') + }) + .data({OxColor: $rightsLevelElement.data('OxColor')}) + .bindEvent({ + change: function(event) { + var rightsLevel = event.value; + $rightsLevelElement = getRightsLevelElement(rightsLevel); + $rightsLevelSelect + .css({background: $rightsLevelElement.css('background')}) + .data({OxColor: $rightsLevelElement.data('OxColor')}) + renderCapabilities(rightsLevel); + pandora.api.edit({id: data.id, rightslevel: rightsLevel}, function(result) { + // ... + }); + } + }) + .appendTo($rightsLevel); + } else { + $rightsLevelElement + .css({ + marginBottom: '4px' + }) + .appendTo($rightsLevel); + } + $capabilities = $('
').appendTo($rightsLevel); + renderCapabilities(data.rightslevel); + } + + function toggleIconSize() { + iconSize = iconSize == 256 ? 512 : 256; + iconWidth = iconRatio > 1 ? iconSize : Math.round(iconSize * iconRatio); + iconHeight = iconRatio < 1 ? iconSize : Math.round(iconSize / iconRatio); + iconLeft = iconSize == 256 ? Math.floor((iconSize - iconWidth) / 2) : 0, + borderRadius = ui.icons == 'posters' ? 0 : iconSize / 8; + $icon.animate({ + left: margin + iconLeft + 'px', + width: iconWidth + 'px', + height: iconHeight + 'px', + borderRadius: borderRadius + 'px' + }, 250); + $reflection.animate({ + top: margin + iconHeight + 'px', + width: iconSize + 'px', + height: iconSize / 2 + 'px' + }, 250); + $reflectionIcon.animate({ + left: iconLeft + 'px', + width: iconWidth + 'px', + height: iconHeight + 'px', + borderRadius: borderRadius + 'px' + }, 250); + $reflectionGradient.animate({ + width: iconSize + 'px', + height: iconSize / 2 + 'px' + }, 250); + $text.animate({ + left: margin + (iconSize == 256 ? 256 : iconWidth) + margin + 'px', + }, 250); + pandora.UI.set({infoIconSize: iconSize}); + } + + that.reload = function() { + var src = src = '/' + data.id + '/' + ( + ui.icons == 'posters' + ? (ui.showSitePosters ? 'siteposter' : 'poster') : 'icon' + ) + '512.jpg?' + Ox.uid() + $icon.attr({src: src}); + $reflectionIcon.attr({src: src}); + iconSize = iconSize == 256 ? 512 : 256; + iconRatio = ui.icons == 'posters' + ? (ui.showSitePosters ? 5/8 : data.posterRatio) : 1; + toggleIconSize(); + pandora.user.level == 'admin' && $list.replaceWith($list = renderList()); + }; + + that.resize = function() { + var height = pandora.$ui.contentPanel.size(1); + $list && $list.css({height: height + 'px'}); + $data.css({height: height + 'px'}); + }; + + that.bindEvent({ + pandora_icons: that.reload, + pandora_showsiteposters: function() { + ui.icons == 'posters' && that.reload(); + } + }); + + return that; + +} diff --git a/static/js/pandora/infoView.js b/static/js/pandora/infoView.js index 84294772..c4a54380 100644 --- a/static/js/pandora/infoView.js +++ b/static/js/pandora/infoView.js @@ -2,65 +2,51 @@ pandora.ui.infoView = function(data) { - // fixme: given that currently, the info view doesn't scroll into view nicely - // when collapsing the movies browser, the info view should become a split panel - var ui = pandora.user.ui, - canEdit = pandora.site.capabilities.canEditMetadata[pandora.user.level], + descriptions = [], + canEdit = pandora.site.capabilities.canEditMetadata[pandora.user.level] || data.editable, css = { marginTop: '4px', textAlign: 'justify', MozUserSelect: 'text', WebkitUserSelect: 'text' }, - iconRatio = ui.icons == 'posters' - ? (ui.showSitePosters ? 5/8 : data.posterRatio) : 1, + html, + iconRatio = ui.icons == 'posters' ? data.posterRatio : 1, iconSize = ui.infoIconSize, iconWidth = iconRatio > 1 ? iconSize : Math.round(iconSize * iconRatio), iconHeight = iconRatio < 1 ? iconSize : Math.round(iconSize / iconRatio), iconLeft = iconSize == 256 ? Math.floor((iconSize - iconWidth) / 2) : 0, borderRadius = ui.icons == 'posters' ? 0 : iconSize / 8, - isEditable = canEdit && data.id.substr(0, 2) == '0x', - listWidth = 144 + Ox.UI.SCROLLBAR_SIZE, margin = 16, statisticsWidth = 128, uid = Ox.uid(), that = Ox.Element(), + $div, $list, - $info = $('
') + $info = Ox.Element() .css({ position: 'absolute', - left: canEdit && !ui.showIconBrowser ? -listWidth + 'px' : 0, + left: 0, top: 0, right: 0, + bottom: 0, + overflowY: 'auto' }) - .appendTo(that.$element), + .appendTo(that), - $data = Ox.Container() - .css({ - position: 'absolute', - left: (canEdit ? listWidth : 0) + 'px', - top: 0, - right: 0, - height: pandora.$ui.contentPanel.size(1) + 'px' - }) + $left = Ox.Element() .appendTo($info), $icon = Ox.Element({ element: '', - tooltip: canEdit ? ( - !ui.showIconBrowser - ? 'Doubleclick to edit icon' - : 'Doubleclick to hide icons' - ) : '' }) .attr({ src: '/' + data.id + '/' + ( - ui.icons == 'posters' - ? (ui.showSitePosters ? 'siteposter' : 'poster') : 'icon' + ui.icons == 'posters' ? 'poster' : 'icon' ) + '512.jpg?' + uid }) .css({ @@ -75,7 +61,7 @@ pandora.ui.infoView = function(data) { .bindEvent({ singleclick: toggleIconSize }) - .appendTo($data.$element), + .appendTo($left), $reflection = $('
') .addClass('OxReflection') @@ -87,19 +73,17 @@ pandora.ui.infoView = function(data) { height: iconSize / 2 + 'px', overflow: 'hidden' }) - .appendTo($data.$element), + .appendTo($left), $reflectionIcon = $('') .attr({ src: '/' + data.id + '/' + ( - ui.icons == 'posters' - ? (ui.showSitePosters ? 'siteposter' : 'poster') : 'icon' + ui.icons == 'posters' ? 'poster' : 'icon' ) + '512.jpg?' + uid }) .css({ position: 'absolute', left: iconLeft + 'px', - bottom: 0, width: iconWidth + 'px', height: iconHeight + 'px', borderRadius: borderRadius + 'px' @@ -114,8 +98,16 @@ pandora.ui.infoView = function(data) { }) .appendTo($reflection), - $text = Ox.Element({ - tooltip: canEdit && !isEditable ? 'Doubleclick to reload metadata' : '' + $data = $('
') + .css({ + position: 'absolute', + left: margin + 'px', + top: margin + iconHeight + margin + 'px', + width: iconSize + 'px', + }) + .appendTo($left), + + $center = Ox.Element({ }) .css({ position: 'absolute', @@ -123,39 +115,105 @@ pandora.ui.infoView = function(data) { top: margin + 'px', right: margin + statisticsWidth + margin + 'px', }) - .bindEvent(canEdit && !isEditable ? { - doubleclick: reloadMetadata - } : {}) - .appendTo($data.$element), + .appendTo($info), - $statistics = $('
') + $right = $('
') .css({ position: 'absolute', width: statisticsWidth + 'px', top: margin + 'px', right: margin + 'px' }) - .appendTo($data.$element), + .appendTo($info), - $capabilities, + $capabilities; - $browserImages = []; + if (!canEdit) { + pandora.createLinks($info); + } - pandora.createLinks($text); // FIXME: this is wrong for editables that already have clickLink + var listKeys = [ + 'language', 'topic', 'director', 'cinematographer', 'featuring', + 'groups', 'license' + ]; + + // Source & Project -------------------------------------------------------- + + var count = 0; + ['source', 'project'].forEach(function(key) { + $div = $('
').appendTo($data); + if (canEdit || data[key]) { + count && $('
').appendTo($div); + $('
') + .css({float: 'left'}) + .html( + formatKey({ + category: 'categories', + }[key] || key).replace('', ' ') + ) + .appendTo($div); + Ox.Editable({ + clickLink: pandora.clickLink, + format: function(value) { + return listKeys.indexOf(key) >= 0 + ? formatValue(value.split(', '), { + 'director': 'name', + 'cinematographer': 'name', + 'features': 'name', + }[key] || key) + : value; + }, + placeholder: formatLight('unknown'), + editable: canEdit, + tooltip: canEdit ? 'Doubleclick to edit' : '', + value: listKeys.indexOf(key) >= 0 + ? (data[key] || []).join(', ') + : data[key] || '' + }) + .css({float: 'left'}) + .bindEvent({ + submit: function(event) { + editMetadata(key, event.value); + } + }) + .appendTo($div); + if (canEdit || data[key + 'description']) { + $('
').css({'clear': 'both'}).appendTo($div); + $('
') + .append( + descriptions[key] = Ox.Editable({ + clickLink: pandora.clickLink, + placeholder: formatLight('No ' + Ox.toTitleCase(key) + ' Description'), + editable: canEdit, + tooltip: canEdit ? 'Doubleclick to edit' : '', + type: 'textarea', + value: data[key + 'description'] || '' + }) + .bindEvent({ + submit: function(event) { + editMetadata(key + 'description', event.value); + } + }) + .css(css) + ).css({ + 'margin-top': '8px', + }) + .appendTo($div); + } + count++; + } + }); // Title ------------------------------------------------------------------- $('
') .css({ - marginTop: '-2px' + marginTop: '-2px', }) .append( Ox.Editable({ - editable: isEditable, - format: function(value) { - return formatTitle(value); - }, - tooltip: isEditable ? 'Doubleclick to edit' : '', + editable: canEdit, + tooltip: canEdit ? 'Doubleclick to edit' : '', value: data.title }) .css({ @@ -171,52 +229,14 @@ pandora.ui.infoView = function(data) { editMetadata('title', event.value); } }) - .appendTo($text) ) - .appendTo($text); + .appendTo($center); - // Director ---------------------------------------------------------------- + // Location, Date, Language and Duration ----------------------------------- - if (data.director || isEditable) { - $('
') - .css({ - marginTop: '2px' - }) - .append( - Ox.Editable({ - clickLink: pandora.clickLink, - editable: isEditable, - format: function(value) { - return formatValue(value.split(', '), 'name'); - }, - placeholder: formatLight('Unknown Director'), - tooltip: isEditable ? 'Doubleclick to edit' : '', - value: data.director ? data.director.join(', ') : 'Unknown Director' - }) - .css({ - display: 'inline-block', - marginBottom: '-3px', - fontWeight: 'bold', - fontSize: '13px', - MozUserSelect: 'text', - WebkitUserSelect: 'text' - }) - .bindEvent({ - submit: function(event) { - editMetadata('director', event.value); - } - }) - ) - .appendTo($text); - } - - // Country, Year, Language, Runtime ---------------------------------------- - - if (isEditable) { - var $div = $('
') - .css(css) - .appendTo($text); - ['country', 'year'].forEach(function(key, i) { + if (canEdit) { + $div = $('
').css(css).css({marginTop: '12px'}).appendTo($center); + ['location', 'date', 'language', 'duration'].forEach(function(key, i) { i && $('
').css({float: 'left'}).html('; ').appendTo($div); $('
') .css({float: 'left'}) @@ -224,14 +244,16 @@ pandora.ui.infoView = function(data) { .appendTo($div); Ox.Editable({ clickLink: pandora.clickLink, + editable: key != 'duration', format: function(value) { - return formatValue(value.split(', '), key) + return formatValue(listKeys.indexOf(key) >= 0 + ? value.split(', ') : value, key); }, placeholder: formatLight('unknown'), tooltip: 'Doubleclick to edit', - value: key == 'country' - ? (data[key] ? data[key].join(', ') : ['']) - : data[key] || '' + value: listKeys.indexOf(key) >= 0 + ? (data[key] || []).join(', ') + : data[key] || '' }) .css({float: 'left'}) .bindEvent({ @@ -241,193 +263,147 @@ pandora.ui.infoView = function(data) { }) .appendTo($div); }); - } else if (data.country || data.year || data.language || data.runtime) { - var html = []; - ['country', 'year', 'language', 'runtime'].forEach(function(key) { + $('
').appendTo($center); + } else { + html = []; + ['location', 'date', 'language', 'duration'].forEach(function(key) { if (data[key]) { - html.push( - formatKey(key) + ( - key != 'runtime' ? formatValue(data[key], key) - : data[key] < 60 ? Math.round(data[key]) + ' sec' - : Math.round(data[key] / 60) + ' min' - ) - ) + html.push(formatKey(key) + formatValue(data[key], key)); } }); - $('
').css(css).html(html.join('; ')).appendTo($text); + $('
').css(css).html(html.join('; ')).appendTo($center); } - // Alternative Titles ------------------------------------------------------ + // Director, Cinematographer and Featuring --------------------------------- - data.alternativeTitles && $('
') - .css(css) - .html( - formatKey('Alternative Title' + (data.alternativeTitles.length == 1 ? '' : 's')) - + data.alternativeTitles.map(function(value) { - return value[0] + (value[1] ? ' ' - + formatLight('(' + value[1] + ')') : ''); - }).join(', ') - ) - .appendTo($text); - - // fixme: episodeDirector seems to be always missing - if (data.episodeDirector || data.writer || data.producer || data.cinematographer || data.editor) { - $div = $('
') - .css(css) - .appendTo($text); - html = []; - ['episodeDirector', 'writer', 'producer', 'cinematographer', 'editor'].forEach(function(key) { - data[key] && html.push( - formatKey(key == 'episodeDirector' ? 'director' : key) + formatValue(data[key], 'name') - ); + if (canEdit) { + $div = $('
').css(css).css('clear', 'both').appendTo($center); + ['director', 'cinematographer', 'featuring'].forEach(function(key, i) { + i && $('
').css({float: 'left'}).html('; ').appendTo($div); + $('
') + .css({float: 'left'}) + .html(formatKey(key).replace('', ' ')) + .appendTo($div); + Ox.Editable({ + clickLink: pandora.clickLink, + format: function(value) { + return formatValue(value.split(', '), 'name'); + }, + placeholder: formatLight('unknown'), + tooltip: 'Doubleclick to edit', + value: listKeys.indexOf(key) >= 0 + ? (data[key] || []).join(', ') + : data[key] || '' + }) + .css({float: 'left'}) + .bindEvent({ + submit: function(event) { + editMetadata(key, event.value); + } + }) + .appendTo($div); }); - $div.html(html.join('; ')); - } - - data.cast && $('
') - .css(css) - .html( - formatKey('cast') + data.cast.map(function(value) { - value.character = value.character.replace('(uncredited)', '').trim(); - return formatValue(value.actor, 'name') - + (value.character ? ' ' - + formatLight('(' + formatValue(value.character) + ')') - : ''); - }).join(', ') - ) - .appendTo($text); - - if (data.genre || data.keyword) { - $div = $('
') - .css(css) - .appendTo($text); + $('
').appendTo($center); + } else if (data.director || data.cinematographer || data.featuring) { html = []; - ['genre', 'keyword'].forEach(function(key) { - data[key] && html.push( - formatKey(key == 'keyword' ? 'keywords' : key) - + formatValue(data[key], key) - ); + ['director', 'cinematographer', 'featuring'].forEach(function(key) { + if (data[key] && data[key].length) { + html.push( + formatKey(key) + + formatValue(data[key], key) + ); + } }); - $div.html(html.join('; ')); + $('
').css(css).html(html.join('; ')).appendTo($center); } - data.summary && $('
') - .css(css) - .html( - formatKey('summary') + data.summary - ) - .appendTo($text); + // Topic ------------------------------------------------------------------- - data.trivia && data.trivia.forEach(function(value) { + if (canEdit) { + var key = 'topic'; + $div = $('
').css(css).css('clear', 'both').appendTo($center); $('
') - .css({ - display: 'table-row' + .css({float: 'left'}) + .html(formatKey('Topics').replace('', ' ')) + .appendTo($div); + Ox.Editable({ + clickLink: pandora.clickLink, + format: function(value) { + return formatValue(value.split(', '), key); + }, + placeholder: formatLight('unknown'), + tooltip: 'Doubleclick to edit', + value: listKeys.indexOf(key) >= 0 + ? (data[key] || []).join(', ') + : data[key] || '' }) - .append( - $('
') - .css({ - display: 'table-cell', - width: '12px', - paddingTop: '4px' - }) - .html('') - ) - .append( - $('
') - .css({ - display: 'table-cell', - paddingTop: '4px', - textAlign: 'justify', - MozUserSelect: 'text', - WebkitUserSelect: 'text' - }) - .html(value) - ) - .append( - $('
').css({clear: 'both'}) - ) - .appendTo($text); - }); - - data.filmingLocations && $('
') - .css(css) - .html( - formatKey('Filming Locations') + data.filmingLocations.map(function(location) { - return '' + location + '' - }).join(', ') - ) - .appendTo($text); - - data.releasedate && $('
') - .css(css) - .html( - formatKey('Release Date') + Ox.formatDate(data.releasedate, '%A, %B %e, %Y') - ) - .appendTo($text); - - if (data.budget || data.gross || data.profit) { - $div = $('
') - .css(css) - .appendTo($text); - html = []; - ['budget', 'gross', 'profit'].forEach(function(key) { - data[key] && html.push( - formatKey(key) + Ox.formatCurrency(data[key], '$') - ); - }); - $div.html(html.join('; ')); + .css({float: 'left'}) + .bindEvent({ + submit: function(event) { + editMetadata(key, event.value); + } + }) + .appendTo($div); + $('
').appendTo($center); + + } else if (data.topic) { + html = formatKey('topics') + formatValue(data.topic, 'topic'); + $('
').css(css).html(html).appendTo($center); } - if (data.rating || data.votes) { - $div = $('
') - .css(css) - .appendTo($text); - html = []; - ['rating', 'votes'].forEach(function(key) { - data[key] && html.push( - formatKey(key) + Ox.formatNumber(data[key], key == 'rating' ? 0 : 2) + '%' - ); - }); - $div.html(html.join('; ')); - } + // Summary ------------------------------------------------------------- - if (data.connections) { - $div = $('
') - .css(css) - .appendTo($text); - html = []; - [ - 'Edited from', 'Edited into', - 'Features', 'Featured in', - 'Follows', 'Followed by', - 'References', 'Referenced in', - 'Remake of', 'Remade as', - 'Spin off from', 'Spin off', - 'Spoofs', 'Spoofed in' - ].forEach(function(key) { - data.connections[key] && html.push( - formatKey(key) + data.connections[key].map(function(connection) { - return connection.item - ? '' + connection.title + '' - : connection.title; - }).join(', ') - ); - }); - $div.html(html.join('; ')); - } - - ['reviews', 'links'].forEach(function(key) { - data[key] && $('
') - .css(css) - .html( - formatKey(key) + data[key].map(function(value) { - return '' + value.source + '' - }).join(', ') + if (canEdit) { + $('
') + .css({marginTop: '16px'}) + .append( + Ox.Editable({ + clickLink: pandora.clickLink, + editable: canEdit, + maxHeight: Infinity, + placeholder: formatLight('No Summary'), + tooltip: canEdit ? 'Doubleclick to edit' : '', + type: 'textarea', + value: data.summary || '', + //width: 300 + }) + .bindEvent({ + submit: function(event) { + editMetadata('summary', event.value); + } + }) + .css(css) ) - .appendTo($text); - }); + .appendTo($center); + } else if(data.summary) { + $('
').css(css).html(data.summary).appendTo($center); + } - $('
').css({height: '16px'}).appendTo($text); + // License ----------------------------------------------------------------- + + $div = $('
').css(css).css({marginTop: '16px'}).appendTo($center); + if (canEdit) { + $('
') + .css({float: 'left'}) + .html(formatKey('license').replace('', ' ')) + .appendTo($div); + Ox.Editable({ + placeholder: formatLight('No License'), + tooltip: 'Doubleclick to edit', + value: (data.license || []).join(', ') + }) + .bindEvent({ + submit: function(event) { + editMetadata('license', event.value); + } + }) + .appendTo($div); + } else if(data.license) { + $div.html( + formatKey('License') + + (data.license || []).join(', ') + ); + } // Hue, Saturation, Lightness, Volume -------------------------------------- @@ -440,20 +416,60 @@ pandora.ui.infoView = function(data) { data[key] || 0, key == 'volume' ? 'lightness' : key ).css({textAlign: 'right'}) ) - .appendTo($statistics); + .appendTo($right); + }); + + // User and Groups --------------------------------------------------------- + + ['user', 'groups'].forEach(function(key) { + (canEdit || data[key] && data[key].length) && $('
') + .css({marginBottom: '4px'}) + .append(formatKey(key, true)) + .append( + $('
') + .css({margin: '2px 0 0 -1px'}) // fixme: weird + .append( + Ox.Editable({ + placeholder: key == 'groups' ? formatLight('No Groups') : '', + editable: canEdit, + tooltip: canEdit ? 'Doubleclick to edit' : '', + value: key == 'user' ? data[key] : data[key].join(', ') + }) + .bindEvent({ + submit: function(event) { + editMetadata(key, event.value); + } + }) + ) + ) + .appendTo($right); + }); + + // Created and Modified ---------------------------------------------------- + + ['created', 'modified'].forEach(function(key) { + $('
') + .css({marginBottom: '4px'}) + .append(formatKey(key, true)) + .append( + $('
').html(Ox.formatDate(data[key], '%B %e, %Y')) + ) + .appendTo($right); }); // Rights Level ------------------------------------------------------------ - var $rightsLevel = $('
'); - $('
') - .css({marginBottom: '4px'}) - .append(formatKey('Rights Level', true)) - .append($rightsLevel) - .appendTo($statistics); - renderRightsLevel(); + if (canEdit) { + var $rightsLevel = $('
'); + $('
') + .css({marginBottom: '4px'}) + .append(formatKey('Rights Level', true)) + .append($rightsLevel) + .appendTo($right); + renderRightsLevel(); + } - // Notes ------------------------------------------------------------------- + // Notes -------------------------------------------------------------------- if (canEdit) { @@ -471,51 +487,34 @@ pandora.ui.infoView = function(data) { }) .bindEvent({ submit: function(event) { - pandora.api.edit({ - id: data.id, - notes: event.value - }, function(result) { - // ... - }); + editMetadata('notes', event.value); } }) ) - .appendTo($statistics); + .appendTo($right); } - $('
').css({height: '16px'}).appendTo($statistics); - - if (canEdit) { - $icon.bindEvent({ - doubleclick: function() { - pandora.UI.set('showIconBrowser', !ui.showIconBrowser); - $info.animate({ - left: ui.showIconBrowser ? 0 : -listWidth + 'px' - }, 250); - $icon.options({ - tooltip: !pandora.user.ui.showIconBrowser - ? 'Doubleclick to edit icon' - : 'Doubleclick to hide icons' - }); - } - }); - renderList(); - } + $('
').css({height: '16px'}).appendTo($right); function editMetadata(key, value) { if (value != data[key]) { var edit = {id: data.id}; if (key == 'title') { - Ox.extend(edit, parseTitle(value)); - } else if(['director', 'country'].indexOf(key) > -1) { + edit[key] = value; + } else if (listKeys.indexOf(key) >= 0) { edit[key] = value ? value.split(', ') : []; } else { - edit[key] = value; + edit[key] = value ? value : null; } pandora.api.edit(edit, function(result) { + data[key] = value; + descriptions[key] && descriptions[key].options({ + value: result.data[key + 'description'] + }); + + Ox.Request.clearCache(); // fixme: too much? can change filter/list etc if (result.data.id != data.id) { - Ox.Request.clearCache(); // fixme: too much pandora.UI.set({item: result.data.id}); pandora.$ui.browser.value(data.id, 'id', result.data.id); } @@ -524,6 +523,14 @@ pandora.ui.infoView = function(data) { pandora.$ui.browser.value(result.data.id, 'director', result.data.director); pandora.$ui.browser.value(result.data.id, 'country', result.data.country); pandora.$ui.browser.value(result.data.id, 'year', result.data.year); + pandora.$ui.itemTitle + .options({ + title: '' + result.data.title + + (Ox.len(result.data.director) + ? ' (' + result.data.director.join(', ') + ')' + : '') + + (result.data.year ? ' ' + result.data.year : '') + '' + }); //pandora.$ui.contentPanel.replaceElement(0, pandora.$ui.browser = pandora.ui.browser()); }); } @@ -539,21 +546,17 @@ pandora.ui.infoView = function(data) { return '' + str + ''; } - function formatTitle(title) { - var match = /(\(S\d{2}E\d{2}\))/.exec(title); - if (match) { - title = title.replace(match[0], formatLight(match[0])); - } - return title + ( - data.originalTitle && data.originalTitle != title - ? ' ' + formatLight('(' + data.originalTitle + ')') : '' - ); - } - function formatValue(value, key) { + if(key == 'date') { + return value ? Ox.formatDate(value, + ['', '%Y', '%B %Y', '%B %e, %Y'][value.split('-').length] + ) : ''; + } else if(key == 'duration') { + return value < 60 ? Math.round(value) + ' sec' : Math.round(value / 60) + ' min'; + } return (Ox.isArray(value) ? value : [value]).map(function(value) { return key ? - '' + value + '' + '' + value + '' : value; }).join(', '); } @@ -567,36 +570,6 @@ pandora.ui.infoView = function(data) { ); } - function parseTitle(title) { - var data = {title: title}, - match = /(\(S(\d{2})E(\d{2})\))/.exec(title), - split; - if (match) { - data.season = parseInt(match[2], 10); - data.episode = parseInt(match[3], 10); - split = title.split(match[1]); - data.seriesTitle = split[0].trim(); - data.episodeTitle = split[1].trim(); - } - return data; - } - - function reloadMetadata() { - var item = ui.item; - // fixme: maybe there's a better method name for this? - pandora.api.updateExternalData({ - id: ui.item - }, function(result) { - //reloading metadata might change results too(i.e. genre) - Ox.Request.clearCache(); - if (ui.item == item && ui.itemView == 'info') { - pandora.$ui.contentPanel.replaceElement( - 1, pandora.$ui.item = pandora.ui.item() - ); - } - }); - } - function renderCapabilities(rightsLevel) { var capabilities = Ox.merge( canEdit ? [{name: 'canSeeItem', symbol: 'Find'}] : [], @@ -645,10 +618,7 @@ pandora.ui.infoView = function(data) { + (hasCapability ? 'can' : 'can\'t') + ' ' + Ox.map(Ox.toSlashes(capability.name).split('/'), function(word, i) { return i == 0 ? null : word.toLowerCase(); - }).join(' ') - .replace('see item', 'see the item') - .replace('play video', 'play the full video') - .replace('download video', 'download the video'), + }).join(' '), title: capability.symbol, type: 'image' }) @@ -675,109 +645,6 @@ pandora.ui.infoView = function(data) { }); } - function renderList() { - pandora.api.get({ - id: data.id, - keys: [ui.icons == 'posters' ? 'posters' : 'frames'] - }, 0, function(result) { - var images = result.data[ui.icons == 'posters' ? 'posters' : 'frames'], - selectedImage = images.filter(function(image) { - return image.selected; - })[0]; - $list = Ox.IconList({ - defaultRatio: ui.icons == 'posters' ? 5/8 : data.stream.aspectratio, - fixedRatio: ui.icons == 'posters' ? false : data.stream.aspectratio, - item: function(data, sort, size) { - var ratio = data.width / data.height; - size = size || 128; - return { - height: ratio <= 1 ? size : size / ratio, - id: data['id'], - info: data.width + ' x ' + data.height + ' px', - title: ui.icons == 'posters' ? data.source : Ox.formatDuration(data.position), - url: data.url.replace('http://', '//'), - width: ratio >= 1 ? size : size * ratio - } - }, - items: images, - keys: ui.icons == 'posters' - ? ['index', 'source', 'width', 'height', 'url'] - : ['index', 'position', 'width', 'height', 'url'], - max: 1, - min: 1, - orientation: 'both', - // fixme: should never be undefined - selected: selectedImage ? [selectedImage['index']] : [], - size: 128, - sort: [{key: 'index', operator: '+'}], - unique: 'index' - }) - .css({ - display: 'block', - position: 'absolute', - left: 0, - top: 0, - width: listWidth + 'px', - height: pandora.$ui.contentPanel.size(1) + 'px' - }) - .bindEvent({ - select: function(event) { - var index = event.ids[0]; - selectedImage = images.filter(function(image) { - return image.index == index; - })[0]; - var imageRatio = selectedImage.width / selectedImage.height, - src = selectedImage.url.replace('http://', '//'); - if ($browserImages.length == 0) { - $browserImages = pandora.$ui.browser.find('img[src*="/' + data.id + '/"]'); - } - if (ui.icons == 'posters' && !ui.showSitePosters) { - $browserImages.each(function() { - var $this = $(this), - size = Math.max($this.width(), $this.height()); - $this.attr({src: src}); - ui.icons == 'posters' && $this.css(imageRatio < 1 ? { - width: Math.round(size * imageRatio) + 'px', - height: size + 'px' - } : { - width: size + 'px', - height: Math.round(size / imageRatio) + 'px' - }); - }); - $icon.attr({src: src}); - $reflectionIcon.attr({src: src}); - iconRatio = imageRatio; - iconSize = iconSize == 256 ? 512 : 256; - toggleIconSize(); - } - pandora.api[ui.icons == 'posters' ? 'setPoster' : 'setPosterFrame'](Ox.extend({ - id: data.id - }, ui.icons == 'posters' ? { - source: selectedImage.source - } : { - // fixme: api slightly inconsistent, this shouldn't be "position" - position: selectedImage.index - }), function() { - // fixme: update the info (video preview) frame as well - var src; - if (ui.icons == 'frames') { - src = '/' + data.id + '/icon512.jpg?' + Ox.uid() - $icon.attr({src: src}); - $reflectionIcon.attr({src: src}); - } - $browserImages.each(function() { - $(this).attr({src: '/' + data.id + '/' + ( - ui.icons == 'posters' ? 'poster' : 'icon' - ) + '128.jpg?' + Ox.uid()}); - }); - }); - } - }) - .appendTo($info); - $list.size(); - }); - } - function renderRightsLevel() { var $rightsLevelElement = getRightsLevelElement(data.rightslevel), $rightsLevelSelect; @@ -848,7 +715,11 @@ pandora.ui.infoView = function(data) { width: iconSize + 'px', height: iconSize / 2 + 'px' }, 250); - $text.animate({ + $data.animate({ + top: margin + iconHeight + margin + 'px', + width: iconSize + 'px', + }, 250); + $center.animate({ left: margin + (iconSize == 256 ? 256 : iconWidth) + margin + 'px', }, 250); pandora.UI.set({infoIconSize: iconSize}); @@ -856,22 +727,18 @@ pandora.ui.infoView = function(data) { that.reload = function() { var src = src = '/' + data.id + '/' + ( - ui.icons == 'posters' - ? (ui.showSitePosters ? 'siteposter' : 'poster') : 'icon' - ) + '512.jpg?' + Ox.uid() + ui.icons == 'posters' ? 'poster' : 'icon' + ) + '512.jpg?' + Ox.uid(); $icon.attr({src: src}); $reflectionIcon.attr({src: src}); iconSize = iconSize == 256 ? 512 : 256; iconRatio = ui.icons == 'posters' ? (ui.showSitePosters ? 5/8 : data.posterRatio) : 1; toggleIconSize(); - pandora.user.level == 'admin' && $list.replaceWith($list = renderList()); }; that.resize = function() { - var height = pandora.$ui.contentPanel.size(1); - $list && $list.css({height: height + 'px'}); - $data.css({height: height + 'px'}); + }; that.bindEvent({