Worked on post uploads (#11) - added GUI

This commit is contained in:
Marcin Kurczewski 2014-09-13 16:11:58 +02:00
parent 7ec3715cdd
commit dfb1198143
8 changed files with 1047 additions and 4 deletions

View file

@ -32,3 +32,12 @@ a {
a:hover {
color: #7b3;
}
hr {
margin: 1.5em auto;
height: 1px;
border: 0;
border-bottom: 1px solid #eee;
border-top: 1px solid #eee;
box-sizing: content-box;
}

View file

@ -8,6 +8,11 @@
display: table-row;
}
.form-label {
width: 1%;
white-space: pre;
}
.form-label,
.form-input {
display: table-cell;
@ -25,6 +30,7 @@
text-align: right;
}
.tag-input,
textarea,
input[type=text],
input[type=password] {
@ -45,7 +51,22 @@ input[type=button] {
background: #eee;
font-family: 'Droid Sans', sans-serif;
font-size: 17px;
cursor: pointer;
}
button:hover,
input[type=button]:hover {
background: #f5f5f5;
}
button.highlight,
input[type=button].highlight {
background: #ad5;
}
button:hover.highlight,
input[type=button]:hover.highlight {
background: #dfa;
}
.file-handler {
border: 3px dashed #eee;
@ -58,3 +79,32 @@ input[type=button] {
border-color: #6a2;
background-color: #eeffcc;
}
.tag-input {
padding: 1px;
line-height: normal !important;
cursor: text;
}
.tag-input ul {
list-style-type: none;
display: inline;
margin: 0;
padding: 0;
}
.tag-input li {
background: #ddd;
display: inline-block;
margin: 1px;
padding: 2px 4px;
font-size: 15px;
}
.tag-input input {
border: none;
width: auto;
}
.tag-input li a {
color: black;
font-size: 14px;
margin-left: 0.5em;
cursor: pointer;
}

View file

@ -0,0 +1,167 @@
#post-upload-step1 {
display: table;
width: 30em;
margin: 0 auto;
}
#post-upload-step1 .file-handler {
padding: 3.5em;
}
#post-upload-step1 .url-handler {
margin-top: 0.5em;
position: relative;
}
#post-upload-step1 .url-handler .input-wrapper {
margin-right: 8.5em;
}
#post-upload-step1 .url-handler button {
position: absolute;
top: 0;
right: 0;
width: 8em;
}
#post-upload-step2 .hybrid-view {
text-align: center;
}
@media all and (min-width: 62.5em) {
#post-upload-step2 .hybrid-window:first-child {
width: 40%;
margin-right: 1em;
float: left;
}
#post-upload-step2 .hybrid-window:last-child {
display: inline-block;
width: 57.5%;
}
}
#post-upload-step2 .thumbnail img {
border: 1px solid black;
vertical-align: middle;
display: block;
}
#post-upload-step2 table {
border-spacing: 0;
table-layout: fixed;
width: 100%;
}
#post-upload-step2 table td,
#post-upload-step2 table th {
padding: 0.2em 0.5em;
text-align: center;
}
#post-upload-step2 table th {
font-weight: normal;
}
#post-upload-step2 table tr.selected {
background: #faffca;
}
#post-upload-step2 table .checkbox {
width: 30px;
padding: 0.2em 0;
}
#post-upload-step2 table .checkbox input {
margin: 0 auto;
}
#post-upload-step2 table .thumbnail {
width: 40px;
padding: 0.2em 0;
}
#post-upload-step2 table .safety {
width: 60px;
padding: 0.2em 0;
}
#post-upload-step2 table .tags {
text-align: left;
overflow: hidden;
text-overflow: ellipsis;
}
#post-upload-step2 table .safety {
text-align: center;
}
#post-upload-step2 table .safety [class^=safety-] {
box-shadow: inset 0 0 0 3px rgba(0,0,0,0.1);
width: 25px;
height: 25px;
margin: 0 auto;
}
#post-upload-step2 table .safety-safe { background: #b2efa2; }
#post-upload-step2 table .safety-sketchy { background: #f0e4a8; }
#post-upload-step2 table .safety-unsafe { background: #fbc6b6; }
#post-upload-step2 table .thumbnail img {
width: 30px;
height: 30px;
display: inline-block;
background-size: 30px 30px;
}
#post-upload-step2 .operations {
list-style-type: none;
margin: 0;
padding: 0;
}
#post-upload-step2 .operations li {
display: inline-block;
margin: 0.3em 0.3em 0 0;
}
#post-upload-step2 form {
width: 100%;
margin: 0 auto;
overflow: hidden;
text-align: left;
}
#post-upload-step2 .messages {
margin-bottom: 1em;
}
#post-upload-step2 .form-slider {
text-align: center;
}
#post-upload-step2 .form-slider .thumbnail img {
max-width: 100%;
max-height: 300px;
margin: 0 auto 1em auto;
}
#post-upload-step2,
.template {
display: none;
}
#lightbox {
display: none;
position: absolute;
pointer-events: none;
position: absolute;
margin-left: 10px;
}
#lightbox img {
max-width: 400px;
max-height: 400px;
background: white;
border: 0.5em solid white;
box-shadow: 0 0 0 1px #eee;
position: relative;
}
#lightbox:after {
content: '';
position: absolute;
left: -8px;
top: 50%;
margin-top: -8px;
width: 12px;
height: 12px;
background: white;
border-left: 1px solid #eee;
border-bottom: 1px solid #eee;
transform: rotate(45deg);
}
#uploading-alert {
display: none;
text-align: left;
}

View file

@ -13,6 +13,7 @@
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.min.js"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/mousetrap/1.4.6/mousetrap.min.js"></script>
<link rel="stylesheet" type="text/css" href="//maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css"/>
<!-- build:template
<link rel="stylesheet" type="text/css" href="app.min.css?<%= timestamp %>"/>
/build -->
@ -25,6 +26,7 @@
<link rel="stylesheet" type="text/css" href="/css/pager.css"/>
<link rel="stylesheet" type="text/css" href="/css/login-form.css"/>
<link rel="stylesheet" type="text/css" href="/css/registration-form.css"/>
<link rel="stylesheet" type="text/css" href="/css/post-upload.css"/>
<link rel="stylesheet" type="text/css" href="/css/user-list.css"/>
<link rel="stylesheet" type="text/css" href="/css/user.css"/>
<!-- /build -->
@ -59,6 +61,7 @@
<script type="text/javascript" src="/js/Util.js"></script>
<script type="text/javascript" src="/js/BrowsingSettings.js"></script>
<script type="text/javascript" src="/js/Controls/FileDropper.js"></script>
<script type="text/javascript" src="/js/Controls/TagInput.js"></script>
<script type="text/javascript" src="/js/PresenterManager.js"></script>
<script type="text/javascript" src="/js/Presenters/TopNavigationPresenter.js"></script>
<script type="text/javascript" src="/js/Presenters/PagedCollectionPresenter.js"></script>

View file

@ -0,0 +1,140 @@
var App = App || {};
App.Controls = App.Controls || {};
//todo: autocomplete
App.Controls.TagInput = function(
$underlyingInput,
_,
jQuery) {
var KEY_RETURN = 13;
var KEY_SPACE = 32;
var KEY_BACKSPACE = 8;
var tags = [];
var options = {
beforeTagAdded: null,
beforeTagRemoved: null,
};
if ($underlyingInput.length !== 1) {
throw new Error('Cannot set tag input to more than one elements at once');
}
if ($underlyingInput.attr('data-tagged')) {
throw new Error('Tag input was already initialized for this element');
}
$underlyingInput.attr('data-tagged', true);
$underlyingInput.hide();
var $wrapper = jQuery('<div class="tag-input">');
var $tagList = jQuery('<ul class="tags">');
var $input = jQuery('<input type="text"/>');
$wrapper.append($tagList);
$wrapper.append($input);
$wrapper.insertAfter($underlyingInput);
$wrapper.click(function(e) {
e.preventDefault();
$input.focus();
});
$input.attr('placeholder', $underlyingInput.attr('placeholder'));
$input.unbind('paste').bind('paste', function(e) {
e.preventDefault();
var pastedText;
if (window.clipboardData) {
pastedText = window.clipboardData.getData('Text');
} else {
pastedText = (e.originalEvent || e).clipboardData.getData('text/plain');
}
var patedTags = pastedText.split(/\s+/);
_.each(patedTags, function(tag) {
addTag(tag);
});
});
$input.unbind('keydown').bind('keydown', function(e) {
if (e.which === KEY_RETURN || e.which === KEY_SPACE) {
e.preventDefault();
var tag = $input.val();
addTag(tag);
$input.val('');
} else if (e.which === KEY_BACKSPACE && jQuery(this).val().length === 0) {
e.preventDefault();
removeLastTag();
}
});
function addTag(tag) {
tag = tag.trim();
if (tag.length === 0) {
return;
}
var oldTags = getTags();
if (_.contains(_.map(oldTags, function(tag) { return tag.toLowerCase(); }), tag.toLowerCase())) {
flashTag(tag);
} else {
if (typeof(options.beforeTagAdded) === 'function') {
options.beforeTagAdded(tag);
}
var newTags = oldTags.slice();
newTags.push(tag);
setTags(newTags);
}
}
function removeTag(tag) {
var oldTags = getTags();
var newTags = _.without(oldTags, tag);
if (newTags.length !== oldTags.length) {
if (typeof(options.beforeTagRemoved) === 'function') {
options.beforeTagRemoved(tag);
}
setTags(newTags);
}
}
function removeLastTag() {
removeTag(_.last(getTags()));
}
function flashTag(tag) {
var $elem = $tagList.find('li[data-tag="' + tag.toLowerCase() + '"]');
$elem.css({backgroundColor: 'rgba(255, 200, 200, 1)'});
}
function setTags(newTags) {
tags = newTags.slice();
$tagList.empty();
$underlyingInput.val(newTags.join(' '));
_.each(newTags, function(tag) {
var $elem = jQuery('<li/>');
$elem.text(tag);
$elem.attr('data-tag', tag.toLowerCase());
var $deleteButton = jQuery('<a><i class="fa fa-remove"></i></a>');
$deleteButton.bind('click', function(e) {
e.preventDefault();
removeTag(tag);
$input.focus();
});
$elem.append($deleteButton);
$tagList.append($elem);
});
}
function getTags() {
return tags;
}
_.extend(options, {
setTags: setTags,
getTags: getTags,
removeTag: removeTag,
addTag: addTag,
});
return options;
};
App.DI.register('tagInput', App.Controls.TagInput);

View file

@ -2,19 +2,547 @@ var App = App || {};
App.Presenters = App.Presenters || {};
App.Presenters.PostUploadPresenter = function(
_,
jQuery,
topNavigationPresenter) {
mousetrap,
promise,
util,
api,
router,
topNavigationPresenter,
messagePresenter) {
var $el = jQuery('#content');
var $messages;
var template;
var allPosts = [];
var tagInput;
var interactionEnabled = true;
function init(args) {
topNavigationPresenter.select('upload');
topNavigationPresenter.changeTitle('Upload');
promise.wait(util.promiseTemplate('post-upload')).then(function(html) {
template = _.template(html);
render();
});
}
function render() {
$el.html('Post upload placeholder');
$el.html(template());
$messages = $el.find('.messages');
new App.Controls.FileDropper($el.find('[name=post-content]'), fileHandlerChanged, jQuery);
$el.find('.url-handler input').keydown(urlHandlerKeyPressed);
$el.find('.url-handler button').click(urlHandlerButtonClicked);
$el.find('thead th.checkbox').click(postTableSelectAllCheckboxClicked);
mousetrap.bind('a', function(e) {
if (!e.altKey && !e.ctrlKey) {
selectPrevPostInTable();
}
}, 'keyup');
mousetrap.bind('d', function(e) {
if (!e.altKey && !e.ctrlKey) {
selectNextPostInTable();
}
}, 'keyup');
$el.find('.remove').click(removeButtonClicked);
$el.find('.move-up').click(moveUpButtonClicked);
$el.find('.move-down').click(moveDownButtonClicked);
$el.find('.submit').click(submitButtonClicked);
tagInput = new App.Controls.TagInput($el.find('form [name=tags]'), _, jQuery);
}
function getDefaultPost() {
return {
safety: 'safe',
source: null,
fileName: null,
anonymous: false,
tags: [],
content: null,
url: null,
thumbnail: null,
$tableRow: null,
};
}
function urlHandlerKeyPressed(e) {
if (e.which !== 13) {
return;
}
e.preventDefault();
$el.find('.url-handler button').trigger('click');
}
function urlHandlerButtonClicked(e) {
var $input = $el.find('.url-handler input');
var url = $input.val().trim();
if (url === '') {
return;
}
var protocol = /^(\w+):\/\//.exec(url);
if (!protocol) {
url = 'http://' + url;
} else {
protocol = protocol[1].toLowerCase();
if (protocol !== 'http' && protocol !== 'https') {
window.alert('Unsupported protocol: ' + protocol);
return;
}
}
$input.val('');
addPostFromUrl(url);
}
function fileHandlerChanged(files) {
for (var i = 0; i < files.length; i ++) {
addPostFromFile(files[i]);
}
}
function addPostFromFile(file) {
var post = _.extend({}, getDefaultPost(), {fileName: file.name});
var reader = new FileReader();
reader.onload = function(e) {
post.content = e.target.result;
if (file.type.match('image.*')) {
post.thumbnail = e.target.result;
postThumbnailLoaded(post);
}
};
reader.readAsDataURL(file);
postAdded(post);
}
function addPostFromUrl(url) {
var post = _.extend({}, getDefaultPost(), {url: url, source: url, fileName: url});
var matches = url.match(/watch.*?=([a-zA-Z0-9_-]+)/);
if (matches) {
var youtubeThumbnailUrl = 'http://img.youtube.com/vi/' + matches[1] + '/mqdefault.jpg';
post.thumbnail = youtubeThumbnailUrl;
} else {
post.thumbnail = url;
}
postAdded(post);
postThumbnailLoaded(post);
}
function postAdded(post) {
var allPosts = getAllPosts();
allPosts.push(post);
setAllPosts(allPosts);
var $table = $el.find('table');
var $row = $table.find('.template').clone(true);
post.$tableRow = $row;
$row.removeClass('template');
$row.find('td:not(.checkbox)').click(postTableRowClicked);
$row.find('td.checkbox').click(postTableCheckboxClicked);
$row.find('img').mouseenter(postTableRowImageHovered);
$row.find('img').mouseleave(postTableRowImageUnhovered);
$row.data('post', post);
$table.find('tbody').append($row);
postChanged(post);
selectPostInTable(post);
showOrHidePostsTable();
}
function postChanged(post) {
var $row = post.$tableRow;
$row.find('.tags').text(post.tags.join(', ') || '-');
$row.find('.safety div').attr('class', 'safety-' + post.safety);
}
function postThumbnailLoaded(post) {
var selectedPosts = getSelectedPosts();
if (selectedPosts.length === 1 && selectedPosts[0] === post && post.thumbnail !== null) {
updatePostThumbnailInForm(post);
}
updatePostThumbnailInTable(post);
}
function updatePostThumbnailInForm(post) {
$el.find('.form-slider .thumbnail img')[0].setAttribute('src', post.thumbnail);
}
function updatePostThumbnailInTable(post) {
var $row = post.$tableRow;
//huge speedup thanks to this condition
if ($row.find('img').attr('src') !== post.thumbnail && post.thumbnail !== null) {
$row.find('img')[0].setAttribute('src', post.thumbnail);
}
}
function postTableRowClicked(e) {
e.preventDefault();
var $allCheckboxes = jQuery(this).parents('table').find('tbody input[type=checkbox]');
var $myCheckbox = jQuery(this).parents('tr').find('input[type=checkbox]');
$allCheckboxes.prop('checked', false);
$myCheckbox.prop('checked', true);
postTableCheckboxesChanged(e);
}
function postTableCheckboxClicked(e) {
if (e.target.nodeName === 'TD') {
var checkbox = jQuery(this).find('input[type=checkbox]');
checkbox.prop('checked', !checkbox.prop('checked'));
}
postTableCheckboxesChanged(e);
}
function postTableSelectAllCheckboxClicked(e) {
var $checkbox = jQuery(this).find('input[type=checkbox]');
if (e.target.nodeName === 'TH') {
$checkbox.prop('checked', !$checkbox.prop('checked'));
}
$el.find('tbody input[type=checkbox]').prop('checked', $checkbox.prop('checked'));
postTableCheckboxesChanged();
}
function postTableCheckboxesChanged(e) {
var $table = $el.find('table');
if (!interactionEnabled) {
if (typeof(e) !== 'undefined') {
e.preventDefault();
}
return;
}
$table.find('tbody tr').each(function(i, row) {
var $row = jQuery(row);
var checked = $row.find('input[type=checkbox]').prop('checked');
$row.toggleClass('selected', checked);
});
var allPosts = getAllPosts();
var selectedPosts = getSelectedPosts();
$table.find('[name=select-all]').prop('checked', allPosts.length === selectedPosts.length);
postTableSelectionChanged(selectedPosts);
}
function getAllPosts() {
return allPosts;
}
function setAllPosts(newPosts) {
allPosts = newPosts;
}
function setAllPostsFromTable() {
var newPosts = _.map($el.find('tbody tr'), function(row) {
var $row = jQuery(row);
return $row.data('post');
});
setAllPosts(newPosts);
}
function getSelectedPosts() {
var selectedPosts = [];
$el.find('tbody tr.selected').each(function(i, row) {
var $row = jQuery(row);
selectedPosts.push($row.data('post'));
});
return selectedPosts;
}
function postTableSelectionChanged(selectedPosts) {
if (selectedPosts.length === 0) {
hidePostEditForm();
} else {
showPostEditForm(selectedPosts);
}
}
function hidePostEditForm() {
var $postEditForm = $el.find('form');
$postEditForm.parent('.form-slider').slideUp(function() {
$postEditForm.find('.thumbnail').hide();
});
}
function showPostEditForm(selectedPosts) {
var $postEditForm = $el.find('form');
$postEditForm.parent('.form-slider').slideDown();
if (selectedPosts.length !== 1) {
$postEditForm.parent('.form-slider').find('.thumbnail').slideUp();
$postEditForm.find('.file-name strong').text('Multiple posts selected');
} else {
var post = selectedPosts[0];
$postEditForm.parent('.form-slider').find('.thumbnail').slideDown();
$postEditForm.find('.file-name strong').text(post.fileName);
updatePostThumbnailInForm(post);
}
var combinedPost = getCombinedPost(selectedPosts);
$postEditForm.find('[name=source]').val(combinedPost.source);
$postEditForm.find('[name=anonymous]').prop('checked', combinedPost.anonymous);
$postEditForm.find('[name=safety]').prop('checked', false);
if (combinedPost.safety !== null) {
$postEditForm.find('[name=safety][value=' + combinedPost.safety + ']').prop('checked', true);
}
tagInput.setTags(combinedPost.tags);
$postEditForm.find('[name=source]').unbind('change').bind('change', function(e) {
setPostsSource(selectedPosts, jQuery(this).val());
});
$postEditForm.find('[name=safety]').unbind('change').bind('change', function(e) {
setPostsSafety(selectedPosts, jQuery(this).val());
});
$postEditForm.find('[name=anonymous]').unbind('change').bind('change', function(e) {
setPostsAnonymity(selectedPosts, jQuery(this).is(':checked'));
});
tagInput.beforeTagAdded = function(tag) {
addTagToPosts(selectedPosts, tag);
};
tagInput.beforeTagRemoved = function(tag) {
removeTagFromPosts(selectedPosts, tag);
};
}
function getCombinedPost(posts) {
var combinedPost = _.extend({}, getDefaultPost());
if (posts.length === 0) {
return combinedPost;
}
_.extend(combinedPost, posts[0]);
var tagFilter = function(post) {
return function(tag) {
return post.tags.indexOf(tag) !== -1;
};
};
for (var i = 1; i < posts.length; i ++) {
if (posts[i].safety !== posts[0].safety) {
combinedPost.safety = null;
}
if (posts[i].anonymous !== posts[0].anonymous) {
combinedPost.anonymous = null;
}
if (posts[i].source !== posts[0].source) {
combinedPost.source = null;
}
combinedPost.tags = combinedPost.tags.filter(tagFilter(posts[i]));
}
return combinedPost;
}
function setPostsSource(posts, newSource) {
_.each(posts, function(post) {
//todo: take care of max source length
post.source = newSource;
postChanged(post);
});
}
function setPostsSafety(posts, newSafety) {
_.each(posts, function(post) {
post.safety = newSafety;
postChanged(post);
});
}
function setPostsAnonymity(posts, isAnonymous) {
_.each(posts, function(post) {
post.anonymous = isAnonymous;
postChanged(post);
});
}
function addTagToPosts(posts, tag) {
jQuery.each(posts, function(i, post) {
var index = post.tags.indexOf(tag);
if (index === -1) {
post.tags.push(tag);
}
});
jQuery.each(posts, function(i, post) {
postChanged(post);
});
}
function removeTagFromPosts(posts, tag) {
jQuery.each(posts, function(i, post) {
var index = post.tags.indexOf(tag);
if (index !== -1) {
post.tags.splice(index, 1);
}
});
jQuery.each(posts, function(i, post) {
postChanged(post);
});
}
function postTableRowImageHovered(e) {
var $img = jQuery(this);
if (!$img.attr('src')) {
return;
}
var $lightbox = jQuery('#lightbox');
$lightbox.find('img').attr('src', $img.attr('src'));
$lightbox
.show()
.css({
left: ($img.position().left + $img.outerWidth()) + 'px',
top: ($img.position().top + ($img.outerHeight() - $lightbox.outerHeight()) / 2) + 'px',
});
}
function postTableRowImageUnhovered(e) {
var $lightbox = jQuery('#lightbox');
$lightbox.hide();
}
function selectPostInTable(post) {
var $table = $el.find('table');
$table.find('tbody input[type=checkbox]').prop('checked', false);
$table.find('tbody tr').each(function(i, row) {
var $row = jQuery(row);
if (post === $row.data('post')) {
$row.find('input[type=checkbox]').prop('checked', true);
return false;
}
});
postTableCheckboxesChanged();
}
function selectPrevPostInTable() {
var prevPost = $el.find('tbody tr.selected:eq(0)').prev().data('post');
if (prevPost) {
selectPostInTable(prevPost);
}
}
function selectNextPostInTable() {
var nextPost = $el.find('tbody tr.selected:eq(0)').next().data('post');
if (nextPost) {
selectPostInTable(nextPost);
}
}
function showOrHidePostsTable() {
if (getAllPosts().length === 0) {
util.disableExitConfirmation();
$el.find('#post-upload-step2').fadeOut();
} else {
util.enableExitConfirmation();
$el.find('#post-upload-step2').fadeIn();
}
}
function removeButtonClicked(e) {
e.preventDefault();
removePosts(getSelectedPosts());
}
function moveUpButtonClicked(e) {
e.preventDefault();
movePostsUp(getSelectedPosts());
}
function moveDownButtonClicked(e) {
e.preventDefault();
movePostsDown(getSelectedPosts());
}
function removePosts(posts) {
_.each(posts, function(post) {
post.$tableRow.remove();
});
setAllPostsFromTable();
showOrHidePostsTable();
postTableCheckboxesChanged();
}
function movePostsUp(posts) {
_.each(posts, function(post) {
var $row = post.$tableRow;
$row.insertBefore($row.prev('tr:not(.selected)'));
});
setAllPostsFromTable();
}
function movePostsDown(posts) {
_.each(posts, function(post) {
var $row = post.$tableRow;
$row.insertAfter($row.next('tr:not(.selected)'));
});
setAllPostsFromTable();
}
function uploadNextPost() {
messagePresenter.hideMessages($messages);
var posts = getAllPosts();
if (posts.length === 0) {
util.disableExitConfirmation();
router.navigate('#/posts');
return;
}
var post = posts[0];
var $row = post.$tableRow;
var formData = {};
if (post.url) {
formData.url = post.url;
} else {
formData.file = post.content;
}
formData.source = post.source;
formData.safety = post.safety;
formData.anonymous = post.anonymous;
formData.tags = post.tags.join(', ');
if (post.tags.length === 0) {
messagePresenter.showError($messages, 'No tags set.');
interactionEnabled = true;
return;
}
promise.wait(api.post('/posts', formData)).then(function(response) {
$row.slideUp(function(response) {
$row.remove();
uploadNextPost();
});
}).fail(function(response) {
messagePresenter.showError($messages, response.json && response.json.error || response);
interactionEnabled = true;
});
}
function submitButtonClicked(e) {
e.preventDefault();
if (!interactionEnabled) {
return;
}
$el.find('tbody input[type=checkbox]').prop('checked', false);
postTableCheckboxesChanged();
messagePresenter.showInfo($messages, 'Uploading in progress&hellip;');
interactionEnabled = false;
uploadNextPost();
}
return {
@ -24,4 +552,4 @@ App.Presenters.PostUploadPresenter = function(
};
App.DI.register('postUploadPresenter', ['jQuery', 'topNavigationPresenter'], App.Presenters.PostUploadPresenter);
App.DI.register('postUploadPresenter', ['_', 'jQuery', 'mousetrap', 'promise', 'util', 'api', 'router', 'topNavigationPresenter', 'messagePresenter'], App.Presenters.PostUploadPresenter);

View file

@ -4,6 +4,16 @@ App.Util = function(_, jQuery, promise) {
var templateCache = {};
function enableExitConfirmation() {
jQuery(window).bind('beforeunload', function(e) {
return 'There are unsaved changes.';
});
}
function disableExitConfirmation() {
jQuery(window).unbind('beforeunload');
}
function parseComplexRouteArgs(args) {
var result = {};
args = (args || '').split(/;/);
@ -136,6 +146,8 @@ App.Util = function(_, jQuery, promise) {
parseComplexRouteArgs: parseComplexRouteArgs,
compileComplexRouteArgs: compileComplexRouteArgs,
formatRelativeTime: formatRelativeTime,
enableExitConfirmation: enableExitConfirmation,
disableExitConfirmation: disableExitConfirmation,
};
};

View file

@ -0,0 +1,134 @@
<div id="post-upload-step1">
<input name="post-content" multiple type="file"/>
<div class="url-handler">
<div class="input-wrapper">
<input type="text" placeholder="Alternatively, paste an URL here." name="url"/>
</div>
<button type="submit">Add URL</button>
</div>
<div class="clear"></div>
</div>
<div id="post-upload-step2">
<hr>
<div class="hybrid-view">
<div class="hybrid-window">
<table>
<thead>
<tr>
<th class="checkbox">
<input type="checkbox" name="select-all"/>
</th>
<th class="thumbnail"></th>
<th class="tags">Tags</th>
<th class="safety">Safety</th>
</tr>
</thead>
<tbody>
</tbody>
<tfoot>
<tr class="template">
<td class="checkbox">
<input type="checkbox"/>
</td>
<td class="thumbnail">
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" alt="Thumbnail"/>
</td>
<td class="tags"></td>
<td class="safety"><div class="safety-template"></div></td>
</tr>
</tfoot>
</table>
<ul class="operations"><!--
--><li>
<button class="remove"><i class="fa fa-remove"></i> Remove</button>
</li><!--
--><li>
<button class="move-up"><i class="fa fa-chevron-up"></i> Move up</button>
</li><!--
--><li>
<button class="move-down"><i class="fa fa-chevron-down"></i> Move down</button>
</li><!--
--><li class="right">
<button class="submit highlight" type="submit"><i class="fa fa-upload"></i> Submit</button>
</li><!--
--></ul>
</div>
<div class="hybrid-window">
<div class="messages"></div>
<div class="form-slider">
<div class="thumbnail">
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" alt="Thumbnail"/>
</div>
<form class="form-wrapper">
<div class="form-row file-name">
<label class="form-label">File:</label>
<div class="form-input">
<strong>filename.jpg</strong>
</div>
</div>
<div class="form-row">
<label class="form-label">Safety:</label>
<div class="form-input">
<label for="post-safety-safe">
<input type="radio" id="post-safety-safe" name="safety" value="safe"/>
Safe
</label>
<label for="post-safety-sketchy">
<input type="radio" id="post-safety-sketchy" name="safety" value="sketchy"/>
Sketchy
</label>
<label for="post-safety-unsafe">
<input type="radio" id="post-safety-unsafe" name="safety" value="unsafe"/>
Unsafe
</label>
</div>
</div>
<div class="form-row">
<label class="form-label" for="post-tags">Tags:</label>
<div class="form-input">
<input type="text" name="tags" id="post-tags" placeholder="Enter some tags&hellip;" value=""/>
</div>
</div>
<div class="form-row">
<label class="form-label" for="post-source">Source:</label>
<div class="form-input">
<input maxlength="200" type="text" name="source" id="post-source" placeholder="Where did you get this? (optional)" value=""/>
</div>
</div>
<div class="form-row">
<label class="form-label" for="post-anonymous">Anonymity:</label>
<div class="form-input">
<label for="post-anonymous">
<input type="checkbox" id="post-anonymous" name="anonymous"/>
Don't show my name in this post
</label>
</div>
</div>
</form>
</div>
</div>
</div>
<div id="lightbox">
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" alt="Preview">
</div>
</div>