first version of new upload interface

This commit is contained in:
j 2016-09-15 15:20:24 +00:00
parent 8feed54bf9
commit 6c013ebc92
20 changed files with 966 additions and 102 deletions

View file

@ -852,6 +852,7 @@
The plug-in architecture is not yet finalized, documentation forthcoming.
*/
"menuExtras": [
"upload",
"user",
//"locale",
"reload"

View file

@ -738,6 +738,7 @@
The plug-in architecture is not yet finalized, documentation forthcoming.
*/
"menuExtras": [
"upload",
"user",
//"locale",
"reload"

View file

@ -672,6 +672,7 @@ examples (config.SITENAME.jsonc) that are part of this pan.do/ra distribution.
The plug-in architecture is not yet finalized, documentation forthcoming.
*/
"menuExtras": [
"upload",
"user",
// "locale",
"reload"

177
static/js/addFilesDialog.js Normal file
View file

@ -0,0 +1,177 @@
'use strict';
pandora.ui.addFilesDialog = function(options) {
var $button = Ox.Button({
id: 'add',
title: Ox._(Ox.toTitleCase(options.action))
}).bindEvent({
click: function() {
$button.options({disabled: true});
(options.action == 'upload' ? uploadVideos : importVideos)(function() {
that.close();
pandora.ui.tasksDialog({
tasks: options.action == 'import' ? 'all' : 'uploads'
}).open();
});
}
});
var $list = Ox.TableList({
columns: [
{
id: 'id',
title: Ox._('ID')
},
{
id: 'index',
title: Ox._('Index')
},
{
format: function(value) {
return Ox.encodeHTMLEntities(value);
},
id: 'title',
title: Ox._('Title'),
visible: true,
width: 256
},
{
format: function(value, data) {
return data.width && data.height
? data.width + ' × ' + data.height + ' px'
: Ox._('unknown')
},
id: 'resolution',
title: Ox._('Resolution'),
align: 'right',
visible: true,
width: 128
},
{
format: function(value) {
return value ? Ox.formatDuration(value) : Ox._('unknown');
},
id: 'duration',
title: Ox._('Duration'),
align: 'right',
visible: true,
width: 128
},
{
format: function(value) {
return value ? Ox.formatValue(value, 'B') : Ox._('unknown');
},
id: 'size',
title: Ox._('Size'),
align: 'right',
visible: true,
width: 128
},
{
id: 'width',
title: Ox._('Width')
},
{
id: 'height',
title: Ox._('Height')
}
],
columnsResizable: true,
columnsVisible: true,
items: options.items.map(function(value, index) {
return Ox.extend(value, {index: index})
}),
scrollbarsVisible: true,
sort: [{key: 'index', operator: '+'}],
sortable: true
}).css({
height: '320px',
width: '640px'
});
var that = Ox.Dialog({
buttons: [$button],
closeButton: true,
content: $list,
height: 320,
removeOnClose: true,
title: Ox._('{0} Video Files', [Ox.toTitleCase(options.action)]),
width: 640
});
var $select = Ox.Select({
items: [
{
id: 'one',
title: Ox._(
'Create one {0} with multiple parts',
[pandora.site.itemName.singular.toLowerCase()]
)
},
{
id: 'multiple',
title: Ox._(
'Create multiple {0}',
[pandora.site.itemName.plural.toLowerCase()]
)
}
],
width: 256
}).css({
display: options.items.length > 1 ? 'block' : 'none',
margin: '4px'
});
$($select.find('.OxButton')[0]).css({margin: '-1px'});
$button.parent().parent().append($select);
function importVideos(callback) {
var id;
Ox.serialForEach(options.items, function(item, index, items, callback) {
var isNewItem = index == 0 || $select.value() == 'multiple';
(isNewItem ? pandora.api.add : Ox.noop)({
title: item.title
}, function(result) {
if (isNewItem) {
id = result.data.id;
}
(isNewItem ? pandora.api.edit : Ox.noop)(Ox.extend(
Ox.filter(item, function(value, key) {
return key != 'title';
}),
{'id': id}
), function(result) {
pandora.api.addMediaUrl({
url: item.url,
item: id
}, callback);
});
});
}, callback);
}
function uploadVideos(callback) {
var id;
Ox.serialForEach(options.items, function(item, index, items, callback) {
var isNewItem = index == 0 || $select.value() == 'multiple';
var title = items[$select.value() == 'one' ? 0 : index].title;
(isNewItem ? pandora.api.add : Ox.noop)({
title: title
}, function(result) {
if (isNewItem) {
id = result.data.id;
}
pandora.uploadQueue.add(Ox.extend(item, {
item: {
id: id,
title: title
}
}));
callback();
});
}, callback);
}
return that;
};

233
static/js/addItemDialog.js Normal file
View file

@ -0,0 +1,233 @@
'use strict';
pandora.ui.addItemDialog = function(options) {
options = options || {};
var input = '';
var selected = options.selected ? options.selected : 'upload';
var $button;
var $panel = Ox.TabPanel({
content: function(id) {
var $content = Ox.Element().css({padding: '8px'});
var $input = Ox.Input({
changeOnKeypress: true,
disabled: id == 'upload',
label: Ox._(id == 'add' ? 'Title' : id == 'upload' ? 'File': 'URL'),
labelWidth: 64,
placeholder: id == 'import' ? Ox._('YouTube, Vimeo, etc.') : '',
width: 512
}).css({
margin: '8px'
}).bindEvent({
change: function(data) {
$button.options({disabled: !data.value});
input = data.value;
}
}).appendTo($content);
return $content;
},
tabs: [
{
id: 'add',
title: Ox._('Add {0}', [pandora.site.itemName.singular]),
disabled: pandora.site.itemRequiresVideo,
selected: selected == 'add'
},
{
id: 'upload',
title: Ox._('Upload Video Files'),
selected: selected == 'upload'
},
{
id: 'import',
title: Ox._('Import Video Files'),
disabled: !pandora.site.capabilities.canImportItems[pandora.user.level],
selected: selected == 'import'
}
]
}).bindEvent({
change: function(data) {
selected = data.selected;
that.options({buttons: [createButton()]});
}
});
var $screen = Ox.LoadingScreen({
size: 16
});
var that = Ox.Dialog({
buttons: [createButton()],
closeButton: true,
content: $panel,
height: 72,
removeOnClose: true,
title: Ox._('Add {0}', [pandora.site.itemName.singular]),
width: 544
});
function createButton() {
$button = Ox[selected == 'upload' ? 'FileButton' : 'Button']({
disabled: selected != 'upload',
id: selected,
title: selected == 'add'
? Ox._('Add {0} Without Video Files', [pandora.site.itemName.singular])
: selected == 'upload' ? Ox._('Select Video Files')
: Ox._('Import Video Files'),
width: selected == 'add' ? 192 : 128
}).bindEvent({
click: function(data) {
if (selected == 'add') {
that.options({content: $screen.start()});
$button.options({disabled: true});
pandora.api.add({title: input}, function(result) {
Ox.Request.clearCache('find');
$screen.stop();
that.close();
pandora.UI.set({
item: result.data.id,
itemView: 'info'
});
});
} else if (selected == 'upload' && data.files.length > 0) {
that.options({content: $screen.start()});
$button.options({disabled: true});
Ox.serialMap(data.files, function(file, index, files, callback) {
getFileInfo(file, function(info) {
callback(Ox.extend(info, {file: file}));
});
}, onInfo);
} else {
that.options({content: $screen.start()});
$button.options({disabled: true});
pandora.api.getMediaUrlInfo({
url: input
}, function(result) {
onInfo(result.data.items);
});
}
}
});
return $button;
}
function getFileInfo(file, callback) {
Ox.oshash(file, function(oshash) {
var $video = $('<video>'),
url = URL.createObjectURL(file),
info = {
audio: [],
direct: false,
oshash: oshash,
name: file.name,
size: file.size,
title: file.name.split('.').slice(0, -1).join('.'),
video: []
};
$video.one('error', function(event) {
callback(info);
});
$video.one('loadedmetadata', function(event) {
info.duration = $video[0].duration;
if ($video[0].videoHeight > 0) {
info.width = $video[0].videoWidth;
info.height = $video[0].videoHeight;
}
if (info.duration) {
info.bitrate = info.size * 8 / info.duration / 1000;
}
callback(info);
});
$video[0].src = url;
});
}
function getVideoInfo(info) {
var values = Ox.map(pandora.site.importMetadata, function(value, key) {
var isArray = Ox.isArray(
Ox.getObjectById(pandora.site.itemKeys, key).type
);
if (isArray && value == '{tags}') {
value = info.tags;
} else {
[
'date', 'description', 'id', 'tags',
'title', 'uploader', 'url'
].forEach(function(infoKey) {
var infoValue = info[infoKey] || '';
if (key == 'year' && infoKey == 'date') {
infoValue = infoValue.substr(0, 4);
}
if (infoKey == 'tags') {
infoValue = infoValue.join(', ');
}
value = value.replace(
new RegExp('\{' + infoKey + '\}', 'g'), infoValue
);
});
// For example: director -> uploader
if (isArray) {
value = [value];
}
}
return value;
});
}
function onInfo(items) {
// FIXME: what about pending/aborted uploads
pandora.api.findMedia({
keys: ['id', 'item', 'url'],
query: {
conditions: selected == 'upload' ? items.map(function(item) {
return {key: 'oshash', operator: '==', value: item.oshash};
}) : items.map(function(item) {
return {key: 'url', operator: '==', value: item.url};
}),
operator: '|'
}
}, function(result) {
if (result.data.items.length) {
Ox.serialMap(result.data.items, function(item, index, items, callback) {
pandora.api.get({
id: item.item,
keys: ['id', 'title']
}, function(result) {
callback(Ox.extend(item, {
itemID: result.data.id,
itemTitle: result.data.title
}));
})
}, function(media) {
media = media.map(function(media) {
return Ox.extend(media, Ox.getObject(
items,
selected == 'upload' ? 'oshash' : 'url',
media[selected == 'upload' ? 'id' : 'url']
));
});
$screen.stop();
that.close();
pandora.ui.mediaExistsDialog({
action: selected,
media: media,
items: items
}).open();
});
} else {
$screen.stop();
that.close();
pandora.ui.addFilesDialog({
action: selected,
items: items
}).open();
}
})
}
return that;
};

View file

@ -67,7 +67,11 @@ pandora.ui.allItems = function(section) {
.css({opacity: canAddItems ? 1 : 0.25})
.hide()
.bindEvent({
click: pandora.addItem
click: function() {
pandora.$ui.addItemDialog = pandora.ui.addItemDialog({
selected: 'add'
}).open();
}
})
.appendTo(that);
$buttons[1] = Ox.Button({
@ -80,7 +84,9 @@ pandora.ui.allItems = function(section) {
.hide()
.bindEvent({
click: function() {
pandora.$ui.uploadVideoDialog = pandora.ui.uploadVideoDialog().open();
pandora.$ui.addItemDialog = pandora.ui.addItemDialog({
selected: 'upload'
}).open();
}
})
.appendTo(that);

View file

@ -1,41 +0,0 @@
// vim: et:ts=4:sw=4:sts=4:ft=javascript
'use strict';
pandora.ui.deleteItemDialog = function(item) {
var that = pandora.ui.iconDialog({
buttons: [
Ox.Button({
id: 'keep',
title: Ox._('Keep {0}', [Ox._(pandora.site.itemName.singular)])
}).bindEvent({
click: function() {
that.close();
}
}),
Ox.Button({
id: 'delete',
title: Ox._('Delete {0}', [Ox._(pandora.site.itemName.singular)])
}).bindEvent({
click: function() {
that.close();
pandora.api.remove({
id: item.id
}, function(result) {
Ox.Request.clearCache();
pandora.UI.set({item: ''});
});
}
})
],
content: Ox._('Are you sure you want to delete the {0} "{1}"?'
+ '<br><br>All data will be removed.',
[Ox._(pandora.site.itemName.singular), item.title]),
keys: {enter: 'delete', escape: 'keep'},
title: Ox._('Delete {0}', [Ox._(pandora.site.itemName.singular)])
});
return that;
};

View file

@ -0,0 +1,55 @@
// vim: et:ts=4:sw=4:sts=4:ft=javascript
'use strict';
pandora.ui.deleteItemsDialog = function(options) {
options = Ox.extend({
items: []
}, options || {});
var that = pandora.ui.iconDialog({
buttons: [
Ox.Button({
id: 'keep',
title: Ox._('Keep {0}', [Ox._(pandora.site.itemName[options.items.length == 1 ? 'singular':'plural'])])
}).bindEvent({
click: function() {
that.close();
}
}),
Ox.Button({
id: 'delete',
title: Ox._('Delete {0}', [Ox._(pandora.site.itemName[options.items.length == 1 ? 'singular':'plural'])])
}).bindEvent({
click: function() {
that.close();
Ox.serialForEach(options.items, function(item, index, items, callback) {
pandora.api.remove({
id: item.id
}, function(result) {
callback();
});
}, function() {
Ox.Request.clearCache();
if (pandora.user.ui.item) {
pandora.UI.set({item: ''});
} else {
pandora.UI.set({listSelection: []});
pandora.reloadList();
}
});
}
})
],
content: options.items.length == 1 ? Ox._('Are you sure you want to delete the {0} "{1}"?'
+ '<br><br>All data will be removed.',
[Ox._(pandora.site.itemName.singular), options.items[0].title])
: Ox._('Are you sure you want to delete {0} {1}?'
+ '<br><br>All data will be removed.',
[options.items.length, Ox._(pandora.site.itemName.plural)]),
keys: {enter: 'delete', escape: 'keep'},
title: Ox._('Delete {0}', [Ox._(pandora.site.itemName[options.items.length == 1 ? 'singular':'plural'])])
});
return that;
};

View file

@ -0,0 +1,12 @@
'use strict';
pandora.ui.editItemDialog = function() {
var that = Ox.Dialog({
closeButton: true,
content: Ox.Element().html('HERE WILL BE DRAGONS').css({'text-align': 'center', 'margin-top': '16px'}),
height: 72,
title: Ox._('Edit {0}', [pandora.site.itemName.singular]),
width: 544
});
return that;
}

View file

@ -64,7 +64,9 @@ pandora.ui.infoView = function(data) {
if (data_.id == 'update') {
updateMetadata();
} else if (data_.id == 'delete') {
pandora.$ui.deleteItemDialog = pandora.ui.deleteItemDialog(data).open();
pandora.$ui.deleteItemsDialog = pandora.ui.deleteItemsDialog({
items: [data]
}).open();
}
}
})

View file

@ -91,9 +91,14 @@ pandora.ui.infoView = function(data) {
} else if (data_.id == 'metadata') {
pandora.$ui.metadataDialog = pandora.ui.metadataDialog(data).open();
} else if (data_.id == 'upload') {
pandora.$ui.uploadVideoDialog = pandora.ui.uploadVideoDialog(data).open();
pandora.$ui.addItemDialog = pandora.ui.addItemDialog({
item: pandora.user.ui.item,
selected: 'upload'
}).open();
} else if (data_.id == 'delete') {
pandora.$ui.deleteItemDialog = pandora.ui.deleteItemDialog(data).open();
pandora.$ui.deleteItemsDialog = pandora.ui.deleteItemsDialog({
items: [data]
}).open();
}
}
})

View file

@ -61,7 +61,9 @@ pandora.ui.infoView = function(data) {
.bindEvent({
click: function(data_) {
if (data_.id == 'delete') {
pandora.$ui.deleteItemDialog = pandora.ui.deleteItemDialog(data).open();
pandora.$ui.deleteItemsDialog = pandora.ui.deleteItemsDialog({
items: [data]
}).open();
}
}
})

View file

@ -61,7 +61,9 @@ pandora.ui.infoView = function(data) {
.bindEvent({
click: function(data_) {
if (data_.id == 'delete') {
pandora.$ui.deleteItemDialog = pandora.ui.deleteItemDialog(data).open();
pandora.$ui.deleteItemsDialog = pandora.ui.deleteItemsDialog({
items: [data]
}).open();
}
}
})

View file

@ -85,7 +85,7 @@ pandora.ui.list = function() {
//Ox.Log('', 'data, pandora.Query.toObject', data, pandora.Query.toObject())
pandora.api.find(Ox.extend(data, {
query: ui.find,
keys: data.keys ? ['modified'].concat(data.keys) : void 0
keys: data.keys ? ['editable', 'modified'].concat(data.keys) : void 0
}), callback);
return Ox.clone(data, true);
},
@ -165,7 +165,7 @@ pandora.ui.list = function() {
}), callback);
return Ox.clone(data, true);
},
keys: ['id', 'modified', 'posterRatio'].concat(pandora.site.itemTitleKeys),
keys: ['id', 'editable', 'modified', 'posterRatio'].concat(pandora.site.itemTitleKeys),
selected: ui.listSelection,
size: 128,
sort: ui.listSort,
@ -241,7 +241,7 @@ pandora.ui.list = function() {
});
return Ox.clone(data, true);
},
keys: ['clips', 'duration', 'id', 'modified', 'posterRatio', 'videoRatio'].concat(pandora.site.itemTitleKeys),
keys: ['clips', 'duration', 'editable', 'id', 'modified', 'posterRatio', 'videoRatio'].concat(pandora.site.itemTitleKeys),
selected: ui.listSelection,
size: 192,
sort: ui.listSort,
@ -358,7 +358,7 @@ pandora.ui.list = function() {
}} : {})), callback);
return Ox.clone(data, true);
},
keys: ['clips', 'duration', 'id', 'modified', 'posterRatio', 'rendered'].concat(pandora.site.itemTitleKeys),
keys: ['clips', 'duration', 'editable', 'id', 'modified', 'posterRatio', 'rendered'].concat(pandora.site.itemTitleKeys),
selected: ui.listSelection,
size: 192,
sort: ui.listSort,

View file

@ -13,7 +13,9 @@ pandora.ui.mainMenu = function() {
lists = {},
that = Ox.MainMenu({
extras: pandora.site.menuExtras.map(function(menuExtra) {
if (menuExtra == 'user') {
if (menuExtra == 'upload') {
return pandora.$ui.uploadButton = pandora.ui.uploadButton();
} else if (menuExtra == 'user') {
return pandora.$ui.userButton = pandora.ui.userButton();
} else if (menuExtra == 'locale') {
return pandora.$ui.localeButton = pandora.ui.localeButton();
@ -42,6 +44,7 @@ pandora.ui.mainMenu = function() {
{ id: 'username', title: Ox._('User: {0}', [isGuest ? 'not signed in' : Ox.encodeHTMLEntities(pandora.user.username)]), disabled: true },
{},
{ id: 'preferences', title: Ox._('Preferences...'), disabled: isGuest, keyboard: 'control ,' },
{ id: 'tasks', title: Ox._('Tasks...'), disabled: isGuest },
{ id: 'archives', title: Ox._('Archives...'), disabled: /*isGuest*/ true },
{},
{ id: 'signup', title: Ox._('Sign Up...'), disabled: !isGuest },
@ -367,6 +370,8 @@ pandora.ui.mainMenu = function() {
})
), data.id)) {
pandora.UI.set({page: data.id});
} else if (data.id == 'tasks') {
pandora.ui.tasksDialog().open();
} else if (Ox.contains([
'newlist', 'newlistfromselection', 'newsmartlist', 'newsmartlistfromresults'
], data.id)) {
@ -384,11 +389,9 @@ pandora.ui.mainMenu = function() {
} else if (data.id == 'editlist') {
pandora.ui.listDialog().open();
} else if (data.id == 'add') {
pandora.addItem();
} else if (data.id == 'upload') {
pandora.$ui.uploadVideoDialog = pandora.ui.uploadVideoDialog().open();
} else if (data.id == 'import') {
pandora.$ui.importMediaDialog = pandora.ui.importMediaDialog().open();
pandora.$ui.addItemDialog = pandora.ui.addItemDialog().open();
} else if (data.id == 'edit') {
pandora.ui.editItemDialog().open();
} else if (data.id == 'deletelist') {
pandora.ui.deleteListDialog().open();
} else if (data.id == 'print') {
@ -465,10 +468,27 @@ pandora.ui.mainMenu = function() {
pandora.clipboard.clear();
} else if (data.id == 'delete') {
if (ui.section == 'items') {
pandora.doHistory('delete', ui.listSelection, ui._list, function() {
pandora.UI.set({listSelection: []});
pandora.reloadList();
});
if (ui._list) {
pandora.doHistory('delete', ui.listSelection, ui._list, function() {
pandora.UI.set({listSelection: []});
pandora.reloadList();
});
} else {
pandora.api.find({
query: {
conditions: ui.listSelection.map(function(id) {
return {key: 'id', value: id, operator: '=='}
}),
operator: '|'
},
keys: ['id', 'title'],
range: [0, ui.listSelection.length]
}, function(result) {
pandora.$ui.deleteItemsDialog = pandora.ui.deleteItemsDialog({
items: result.data.items
}).open();
});
}
} else if (ui.section == 'edits') {
var clips = pandora.$ui.editPanel.getSelectedClips();
pandora.doHistory('delete', clips, ui.edit, function(result) {
@ -987,6 +1007,7 @@ pandora.ui.mainMenu = function() {
function getItemMenu() {
var listData = pandora.getListData(),
deleteVerb = ui._list ? Ox._('Remove') : Ox._('Delete'),
isEditable = listData.editable && listData.type == 'static',
isClipView = pandora.isClipView()
&& pandora.$ui.clipList
@ -999,7 +1020,9 @@ pandora.ui.mainMenu = function() {
isEditView = ui.section == 'edits' && ui.edit
&& ui.editView != 'annotations', // FIXME: focus
listName = isVideoView || isClipView ? ''
: ui.section == 'items' ? Ox._('from List')
: ui.section == 'items' ? (
ui._list ? Ox._('from List') : Ox._('from Archive')
)
: Ox._('from Edit'),
listItemsName = Ox._(
ui.section == 'edits' || isVideoView || isClipView ? 'Clips'
@ -1026,6 +1049,24 @@ pandora.ui.mainMenu = function() {
: clipboardType == 'clip' ? (clipboardItems == 1 ? 'Clip' : 'Clips')
: ''
),
canEdit = pandora.site.capabilities.canEditMedia[pandora.user.level] || (
ui.section == 'items' && (
ui.item || (
Ox.contains(['list', 'grid', 'clips', 'timelines'], ui.listView)
&& ui.listSelection.length == 1
)
) && pandora.$ui.list.value(ui.listSelection[0], 'editable')
),
canDelete = pandora.site.capabilities.canRemoveItems[pandora.user.level] || (
ui.section == 'items' && (
ui.item || (
Ox.contains(['list', 'grid', 'clips', 'timelines'], ui.listView)
&& ui.listSelection.length
)
) && ui.listSelection.every(function(item) {
return pandora.$ui.list.value(item, 'editable');
})
),
canSelect = isListView || isClipView || isEditView,
canCopy = isListView ? ui.listSelection.length
: isClipView ? pandora.$ui.clipList.options('selected').length
@ -1042,9 +1083,8 @@ pandora.ui.mainMenu = function() {
undoText = pandora.history.undoText(),
redoText = pandora.history.redoText();
return { id: 'itemMenu', title: Ox._('Item'), items: [
{ id: 'add', title: Ox._('Add {0}', [Ox._(pandora.site.itemName.singular)]), disabled: pandora.site.itemRequiresVideo || !pandora.site.capabilities.canAddItems[pandora.user.level] },
{ id: 'upload', title: Ox._('Upload Video...'), disabled: !pandora.site.capabilities.canAddItems[pandora.user.level] },
{ id: 'import', title: Ox._('Import Video...'), disabled: !pandora.site.capabilities.canImportItems[pandora.user.level] },
{ id: 'add', title: Ox._('Add {0}...', [Ox._(pandora.site.itemName.singular)]), disabled: !pandora.site.capabilities.canAddItems[pandora.user.level] },
{ id: 'edit', title: Ox._('Edit {0}...', [Ox._(pandora.site.itemName.singular)]), disabled: true /*fixme: !canEdit */ },
{},
{ id: 'selectall', title: Ox._('Select All {0}', [listItemsName]), disabled: !canSelect, keyboard: 'control a' },
{ id: 'selectnone', title: Ox._('Select None'), disabled: !canSelect, keyboard: 'shift control a' },
@ -1057,7 +1097,7 @@ pandora.ui.mainMenu = function() {
{ id: 'paste', title: clipboardItems == 0 ? Ox._('Paste') : Ox._('Paste {0}', [clipboardItemName]), disabled: !canPaste, keyboard: 'control v' },
{ id: 'clearclipboard', title: Ox._('Clear Clipboard'), disabled: !clipboardItems},
{},
{ id: 'delete', title: Ox._('Delete {0} {1}', [selectionItemName, listName]), disabled: !canCut, keyboard: 'delete' },
{ id: 'delete', title: Ox._('{0} {1} {2}', [deleteVerb, selectionItemName, listName]), disabled: !canDelete, keyboard: 'delete' },
{},
{ id: 'undo', title: undoText ? Ox._('Undo {0}', [undoText]) : Ox._('Undo'), disabled: !undoText, keyboard: 'control z' },
{ id: 'redo', title: redoText ? Ox._('Redo {0}', [redoText]) : Ox._('Redo'), disabled: !redoText, keyboard: 'shift control z' },

View file

@ -0,0 +1,80 @@
'use strict';
pandora.ui.mediaExistsDialog = function(options) {
var title = options.media.length == 1 ? 'File Exists' : 'Files Exist';
var $content = Ox.Element().addClass('OxText').css({
margin: '16px',
overflow: 'auto'
}).html(
'<p>' + (
options.media.length == 1 && options.items.length == 1
? Ox._('The video file already exists:')
: options.media.length == options.items.length
? Ox._(
'All {0} video files already exist:',
[options.media.length]
)
: Ox._(
'{0} of the {1} video files already exist:',
[options.media.length, options.items.length])
) + '</p>' + options.media.map(function(media) {
return media[options.action == 'upload' ? 'name' : 'title']
+ ': <a href="/' + media.itemID + '" target="_blank">'
+ media.itemTitle + '</a>';
}).join('<br/>')
)
var $buttons = options.media.length == options.items.length ? [
Ox.Button({
id: 'close',
title: Ox._('Close')
}).bindEvent({
click: function() {
that.close();
}
})
] : [
Ox.Button({
id: 'cancel',
title: Ox._('Don\'t {0}', [Ox.toTitleCase(options.action)])
}).bindEvent({
click: function() {
that.close();
}
}),
Ox.Button({
id: 'continue',
title: Ox._(Ox.toTitleCase(options.action)) + ' '
+ (options.items.length - options.media.length) + ' '
+ Ox._(options.items.length - options.media.length == 1 ? 'File' : 'Files')
}).bindEvent({
click: function() {
var existing = options.media.map(function(item) {
return item.id;
}),
items = options.items.filter(function(item) {
return existing.indexOf(item.oshash) == -1;
});
pandora.ui.addFilesDialog({
action: options.action,
items: items
}).open();
}
})
];
var that = Ox.Dialog({
buttons: $buttons,
closeButton: true,
content: $content,
height: 256,
removeOnClose: true,
title: title,
width: 512,
});
return that;
};

View file

@ -518,7 +518,9 @@ pandora.ui.mediaView = function(options) {
id: pandora.user.ui.item,
keys: ['id', 'title']
},function(result) {
pandora.ui.deleteItemDialog(result.data).open();
pandora.$ui.deleteItemsDialog = pandora.ui.deleteItemsDialog({
items: [result.data]
}).open();
});
}

View file

@ -1,10 +1,15 @@
'use strict';
pandora.ui.tasksDialog = function() {
pandora.ui.tasksDialog = function(options) {
options = Ox.extend({
tasks: 'all'
}, options || {});
var canceling = [],
timeout,
clientItems = [], serverItems = [], listItems = [],
clientTimeout, serverTimeout,
$list = Ox.TableList({
columns: [
@ -13,6 +18,16 @@ pandora.ui.tasksDialog = function() {
title: Ox._('ID'),
visible: false
},
{
format: function(value) {
return Ox.encodeHTMLEntities(value);
},
id: 'user',
operator: '+',
title: Ox._('User'),
visible: false,
width: 144
},
{
format: function(value) {
return Ox.encodeHTMLEntities(value);
@ -21,7 +36,7 @@ pandora.ui.tasksDialog = function() {
operator: '+',
title: Ox._('Title'),
visible: true,
width: 256
width: 288
},
{
format: function(value) {
@ -49,62 +64,129 @@ pandora.ui.tasksDialog = function() {
width: 144
},
{
format: function(value) {
return Ox.toTitleCase(value);
format: function(value, data) {
return {
'pending': 'Uploading (Queued)',
'uploading': 'Uploading' + (
data.progress === void 0 ? '' : ' (' + (
data.progress === 0 ? 'Queued'
: data.progress + '%'
) + ')'
),
'queued': $tasksSelect.value() == 'all'
? 'Processing (Queued)'
: 'Finished',
'processing': 'Processing',
'canceled': 'Canceled',
'failed': 'Failed',
'finished': 'Finished'
}[value] || value;
},
id: 'status',
operator: '+',
sort: function(value) {
return [
'queued', 'uploading', 'importing', 'processing',
'pending', 'uploading', 'queued', 'processing',
'canceled', 'failed', 'finished'
].indexOf(value);
},
title: Ox._('Status'),
visible: true,
width: 96
width: 176
},
{
id: 'progress',
title: Ox._('Progress'),
visible: false
}
],
columnsVisible: true,
items: [],
items: listItems,
sort: [{key: 'ended', operator: '-'}],
unique: 'id'
}).bindEvent({
select: updateButton
select: updateButton,
open: function(data) {
var item;
if (data.ids.length == 1) {
item = listItems.filter(function(item) {
return item.id == data.ids[0];
})[0];
if (item && item.item) {
that.close();
pandora.UI.set({
item: item.item,
itemView: 'info'
});
}
}
}
}),
$sidebar = Ox.Element().css({
margin: '4px'
}),
$checkbox = Ox.Checkbox({
title: Ox._('Show All Tasks')
$tasksSelect = Ox.Select({
items: [
{id: 'uploads', title: Ox._('Show Uploads')},
{id: 'all', title: Ox._('Show All Tasks')}
],
value: options.tasks,
width: 128
}).css({
display: true || pandora.site.capabilities.canSeeAllTasks[
pandora.user.level
] ? 'block' : 'none',
margin: '4px'
}).bindEvent({
change: getItems
change: updateList
})
.appendTo($sidebar),
$usersSelect = Ox.Select({
items: [
{id: 'my', title: Ox._('Show My Tasks')},
{id: 'all', title: Ox._('Show All Users')}
],
width: 128
}).css({
display: pandora.site.capabilities.canSeeAllTasks[
pandora.user.level
] ? 'block' : 'none',
margin: '8px 4px 4px 4px'
}).bindEvent({
change: function(data) {
$list[data.value == 'all' ? 'addColumn' : 'removeColumn']('user');
$list.resizeColumn('title', data.value == 'all' ? 144 : 288);
setTimeout(updateList);
}
})
.appendTo($sidebar),
$button = Ox.Button({
disabled: true,
title: Ox._('Cancel Task'),
width: 112
width: 128
}).css({
margin: '4px',
}).bindEvent({
click: function() {
$button.options({disabled: true});
var ids = $list.options('selected').filter(canBeCanceled);
canceling.push(ids);
$button.options({disabled: true});
ids.forEach(function(id) {
if (Ox.contains(
['pending', 'uploading'], $list.value(id, 'status')
)) {
pandora.uploadQueue.remove(id);
}
})
pandora.api.cancelTask({
id: ids
}, function() {
canceling = [];
getItems();
canceling = canceling.filter(function(id) {
return !Ox.contains(ids, id);
});
getServerItems();
});
}
}).appendTo($sidebar),
@ -113,7 +195,7 @@ pandora.ui.tasksDialog = function() {
elements: [
{
element: $list,
size: 640
size: 752
},
{
element: $sidebar,
@ -137,13 +219,16 @@ pandora.ui.tasksDialog = function() {
content: $panel,
height: 384,
title: Ox._('Tasks'),
width: 768
width: 896
})
.bindEvent({
close: function() {
clearTimeout(timeout);
clientTimeout && clearTimeout(clientTimeout);
},
open: getItems
open: function() {
getClientItems();
getServerItems();
}
});
function canBeCanceled(id) {
@ -153,15 +238,77 @@ pandora.ui.tasksDialog = function() {
) && !Ox.contains(canceling, id);
}
function getItems() {
clearTimeout(timeout);
pandora.api.getTasks($checkbox.value() ? {} : {
function getClientItems(callback) {
clearTimeout(clientTimeout);
var uploads = pandora.uploadQueue.get();
var uploadsById = {};
uploads.forEach(function(upload) {
uploadsById[upload.item.id] = (
uploadsById[upload.item.id] || []
).concat(upload);
});
clientItems = []
Ox.forEach(uploadsById, function(uploads, id) {
// FIXME: include upload.file.size
var progress = Math.round(Ox.sum(uploads.map(function(upload) {
return upload.data.progress / uploads.length;
})) * 100);
var status = uploads.map(function(upload) {
return upload.data.status;
});
clientItems.push({
ended: uploads.every(function(upload) {
return upload.data.ended;
}) ? Ox.max(uploads.map(function(upload) {
return upload.data.ended;
})) : '',
id: id,
progress: progress,
started: Ox.min(uploads.map(function(upload) {
return upload.data.started;
})),
status: Ox.contains(status, 'uploading') ? 'uploading'
: Ox.contains(status, 'pending') ? 'pending'
: Ox.contains(status, 'canceled') ? 'canceled'
: 'queued',
title: uploads[0].item.title,
user: pandora.user.username
});
});
clientTimeout = setTimeout(getClientItems, 1000);
updateValues();
callback && callback();
}
function getListItems() {
var allTasks = $tasksSelect.value() == 'all',
uploading = allTasks
? ['pending', 'uploading']
: ['pending', 'uploading', 'queued'],
items = clientItems.filter(function(item) {
return Ox.contains(uploading, item.status);
}),
items = serverItems.filter(function(item) {
return allTasks ? (
Ox.getIndex(items, 'id', item.item) == -1
|| item.user != pandora.user.username
) : (
Ox.getIndex(items, 'id', item.item) == -1
&& Ox.getIndex(clientItems, 'id', item.item) > -1
);
}).concat(items);
return items;
}
function getServerItems(callback) {
clearTimeout(serverTimeout);
pandora.api.getTasks($usersSelect.value() == 'all' ? {} : {
user: pandora.user.username
}, function(result) {
$list.options({items: result.data.items})
updateButton()
serverItems = result.data.items;
serverTimeout = setTimeout(getServerItems, 10000);
getClientItems(callback);
});
timeout = setTimeout(getItems, 15000);
}
function updateButton() {
@ -172,6 +319,31 @@ pandora.ui.tasksDialog = function() {
});
}
function updateList() {
getServerItems(function() {
listItems= getListItems();
$list.options({items: listItems});
updateButton();
});
}
function updateValues() {
var currentListItems = getListItems(),
hasNewItems = currentListItems.length != listItems.length;
!hasNewItems && currentListItems.forEach(function(item) {
if (Ox.getIndexById(listItems, item.id) == -1) {
hasNewItems = true;
} else if (!hasNewItems) {
$list.value(item.id, 'progress', item.progress);
$list.value(item.id, 'status', item.status);
if (item.ended) {
$list.value(item.id, 'ended', item.ended);
}
}
});
hasNewItems && updateList();
}
return that;
};

27
static/js/uploadButton.js Normal file
View file

@ -0,0 +1,27 @@
'use strict';
pandora.ui.uploadButton = function() {
var that = Ox.Button({
style: 'symbol',
title: 'upload',
type: 'image'
}).css({
display: 'none'
}).bindEvent({
click: function() {
pandora.ui.tasksDialog({
tasks: 'uploads'
}).open();
}
});
that.update = function() {
that.css({
display: pandora.uploadQueue.uploading ? 'block' : 'none'
});
};
return that;
};

View file

@ -2,13 +2,15 @@
'use strict';
pandora.addItem = function() {
pandora.api.add(function(result) {
pandora.addItem = function(title, callback) {
// unused
pandora.api.add({title: title || 'Untitled'}, function(result) {
Ox.Request.clearCache('find');
pandora.UI.set({
item: result.data.id,
itemView: 'info'
});
callback && callback();
});
};
@ -257,6 +259,12 @@ pandora.beforeUnloadWindow = function() {
+ 'Are you sure that you want to leave this page?'
);
}
if (pandora.uploadQueue.uploading) {
return Ox._(
'You still have active uploads. '
+ 'Are you sure that you want to leave this page?'
);
}
// Prevent error dialogs on unload
pandora.isUnloading = true;
};
@ -2272,9 +2280,11 @@ pandora.reloadList = function() {
// (but then it's also unlikely they'll have to be reloaded)
var folder = listData.status != 'featured'
? 'personal' : 'featured';
pandora.$ui.folderList[folder].value(
listData.id, 'items', data.items
);
if (!Ox.isEmpty(listData)) {
pandora.$ui.folderList[folder].value(
listData.id, 'items', data.items
);
}
}
})
.bindEventOnce({
@ -2639,12 +2649,89 @@ pandora.updateStatus = function(item) {
return ui.item == item && [
'info', 'player', 'editor', 'timeline'
].indexOf(ui.itemView) > -1 && !(
// fixme: still wrong
pandora.$ui.uploadVideoDialog
&& pandora.$ui.uploadVideoDialog.is('::visible')
);
}
};
pandora.uploadQueue = (function() {
var that = {
uploading: false
};
var queue = [];
var index = -1;
var uploader;
function upload() {
if (index == queue.length - 1) {
that.uploading = false;
pandora.$ui.uploadButton && pandora.$ui.uploadButton.update();
return;
}
index++;
if (queue[index].data.status == 'canceled') {
upload();
return;
}
that.uploading = true;
queue[index].data.status = 'uploading';
pandora.$ui.uploadButton && pandora.$ui.uploadButton.update();
pandora.api.addMedia({
filename: queue[index].file.name,
id: queue[index].oshash,
item: queue[index].item.id
}, function(result) {
uploader = pandora.chunkupload({
data: {id: queue[index].oshash},
file: queue[index].file,
url: '/api/upload/direct/'
}).bindEvent({
done: function(data) {
queue[index].data.ended = +new Date();
queue[index].data.status = data.progress == 1 ? 'queued' : 'failed';
queue[index].data.progress = data.progress;
upload();
},
progress: function(data) {
queue[index].data.progress = data.progress;
}
});
that.uploading = queue[index].item.id;
});
}
that.add = function(items) {
items = Ox.isArray(items) ? items : [items];
queue = queue.concat(items.map(function(item) {
return Ox.extend(item, {
data: {
progress: 0,
started: +new Date(),
status: 'pending'
}
});
}));
!that.uploading && upload();
};
that.get = function() {
return queue;
};
that.remove = function(id) {
queue.forEach(function(item, index) {
if (item.item.id == id) {
queue[index].data.status = 'canceled';
}
});
if (that.uploading == id) {
uploader.abort();
upload();
}
};
return that;
}());
pandora.wait = function(id, callback, timeout) {
var task = {};
timeout = timeout || 5000;