16 Commits
1.0.1 ... 1.0.2

Author SHA1 Message Date
43334b33e1 Fixed suggestions not hiding in the upload 2014-12-14 20:49:42 +01:00
839e97b1e2 Fixed history for posts uploaded anonymously
Until now posts uploaded anonymously were not so anonymous - the author
was clearly visible in post history (and tag history if the upload has
led to creation of any new tags). Both of these are fixed now.
2014-12-14 09:30:22 +01:00
8a33b9581d Tweaked font size for <small/> tag 2014-12-07 13:43:15 +01:00
7067d8e13d Added support for [small]text[/small] 2014-12-07 13:43:04 +01:00
5769034223 Fixed tag input behavior for initial text 2014-11-30 20:30:06 +01:00
c0a5c800e0 Fixed autocomplete staying even after hiding input 2014-11-30 13:05:31 +01:00
b70231bd7e Changed used tags in autocomplete to be grayed-out 2014-11-30 12:49:48 +01:00
bc757dd883 Fixed reused implied tags marked as duplicates 2014-11-30 12:49:48 +01:00
924592675c Fixed tag suggestions for implied tags 2014-11-30 12:40:22 +01:00
a3aea27a13 Fixed tag edits not triggering tag list updates 2014-11-30 12:40:22 +01:00
3c54671aeb Widened post edit form 2014-11-30 11:58:39 +01:00
d8df51f0c0 Moved suggestions before siblings 2014-11-30 11:55:24 +01:00
b693a5f4b3 Added tag siblings suggestion synchronization 2014-11-30 11:54:38 +01:00
303f91e15c Refactored tag suggestions 2014-11-30 11:45:49 +01:00
c350c47195 Added showing tag suggestions on click 2014-11-30 11:31:40 +01:00
24ce67b4ff Added auto completion to tag list presenter 2014-11-30 00:22:50 +01:00
10 changed files with 172 additions and 97 deletions

View File

@ -24,6 +24,10 @@ h3 {
font-size: 20px;
}
small {
font-size: 13px;
}
#middle {
padding: 0 2em;
position: relative;

View File

@ -18,10 +18,12 @@
#home .post .left {
display: inline-block;
float: left;
margin-right: 0.5em;
}
#home .post .right {
display: inline-block;
float: right;
margin-left: 0.5em;
}
#home .post-footer,
@ -35,5 +37,7 @@
#home .version {
opacity: .4;
font-size: 12px;
}
#home .subheader, #home .post-footer {
font-size: 85%;
}

View File

@ -157,12 +157,17 @@
#post-view #post-edit-target {
padding: 1em;
width: 50%;
min-width: 30em;
position: absolute;
background: rgba(255, 255, 255, 0.8);
box-shadow: 0 0 1em 0.5em rgba(255, 255, 255, 0.8);
z-index: 2;
display: none;
}
#post-edit-target .form-wrapper {
min-width: 100%;
}
#post-view>* {
z-index: -1;
}
@ -224,6 +229,7 @@
position: absolute;
background: rgba(255, 255, 255, 0.3);
border: 1px solid rgba(0, 0, 0, 0.3);
font-size: 12pt;
}
.post-note .text-wrapper {
position: absolute;

View File

@ -17,12 +17,14 @@ App.Controls.AutoCompleteInput = function($input) {
maxResults: 15,
minLengthToArbitrarySearch: 3,
onApply: null,
onRender: null,
additionalFilter: null,
};
var showTimeout = null;
var cachedSource = null;
var results = [];
var activeResult = -1;
var monitorInputHidingInterval = null;
if ($input.length === 0) {
throw new Error('Input element was not found');
@ -133,6 +135,7 @@ App.Controls.AutoCompleteInput = function($input) {
function hide() {
$div.hide();
window.clearInterval(monitorInputHidingInterval);
}
function selectPrevious() {
@ -222,12 +225,16 @@ App.Controls.AutoCompleteInput = function($input) {
});
$list.append($listItem);
});
if (options.onRender) {
options.onRender($list);
}
refreshActiveResult();
$div.css({
left: ($input.offset().left) + 'px',
top: ($input.offset().top + $input.outerHeight() - 2) + 'px',
});
$div.show();
monitorInputHiding();
}
function refreshActiveResult() {
@ -237,5 +244,13 @@ App.Controls.AutoCompleteInput = function($input) {
}
}
function monitorInputHiding() {
monitorInputHidingInterval = window.setInterval(function() {
if (!$input.is(':visible')) {
hide();
}
}, 100);
}
return options;
};

View File

@ -14,6 +14,14 @@ App.Controls.TagInput = function($underlyingInput) {
var tagConfirmKeys = [KEY_RETURN, KEY_SPACE];
var inputConfirmKeys = [KEY_RETURN];
var SOURCE_INITIAL_TEXT = 1;
var SOURCE_AUTOCOMPLETION = 2;
var SOURCE_PASTE = 3;
var SOURCE_IMPLICATIONS = 4;
var SOURCE_INPUT_BLUR = 5;
var SOURCE_INPUT_ENTER = 6;
var SOURCE_SUGGESTIONS = 7;
var tags = [];
var options = {
beforeTagAdded: null,
@ -56,10 +64,10 @@ App.Controls.TagInput = function($underlyingInput) {
$input.focus();
});
$input.attr('placeholder', $underlyingInput.attr('placeholder'));
$suggestions.insertAfter($wrapper);
$siblings.insertAfter($wrapper);
$suggestions.insertAfter($wrapper);
processText($underlyingInput.val(), addTagDirectly);
processText($underlyingInput.val(), SOURCE_INITIAL_TEXT);
$underlyingInput.val('');
}
@ -67,7 +75,7 @@ App.Controls.TagInput = function($underlyingInput) {
function initAutoComplete() {
var autoComplete = new App.Controls.AutoCompleteInput($input);
autoComplete.onApply = function(text) {
processText(text, addTag);
processText(text, SOURCE_AUTOCOMPLETION);
$input.val('');
};
autoComplete.additionalFilter = function(results) {
@ -75,6 +83,14 @@ App.Controls.TagInput = function($underlyingInput) {
return !_.contains(getTags(), resultItem[0]);
});
};
autoComplete.onRender = function($list) {
$list.find('li').each(function() {
var $li = jQuery(this);
if (isTaggedWith($li.attr('data-key'))) {
$li.css('opacity', '0.5');
}
});
};
}
$input.bind('focus', function(e) {
@ -83,7 +99,7 @@ App.Controls.TagInput = function($underlyingInput) {
$input.bind('blur', function(e) {
$wrapper.removeClass('focused');
var tagName = $input.val();
addTag(tagName);
addTag(tagName, SOURCE_INPUT_BLUR);
$input.val('');
});
@ -101,7 +117,7 @@ App.Controls.TagInput = function($underlyingInput) {
return;
}
processTextWithoutLast(pastedText, addTag);
processTextWithoutLast(pastedText, SOURCE_PASTE);
});
$input.bind('keydown', function(e) {
@ -114,7 +130,7 @@ App.Controls.TagInput = function($underlyingInput) {
var tagName = $input.val();
e.preventDefault();
$input.val('');
addTag(tagName);
addTag(tagName, SOURCE_INPUT_ENTER);
} else if (e.which === KEY_BACKSPACE && jQuery(this).val().length === 0) {
e.preventDefault();
removeLastTag();
@ -127,19 +143,19 @@ App.Controls.TagInput = function($underlyingInput) {
});
}
function processText(text, callback) {
function processText(text, source) {
var tagNamesToAdd = explodeText(text);
_.map(tagNamesToAdd, function(tagName) { callback(tagName); });
_.map(tagNamesToAdd, function(tagName) { addTag(tagName, source); });
}
function processTextWithoutLast(text, callback) {
function processTextWithoutLast(text, source) {
var tagNamesToAdd = explodeText(text);
var lastTagName = tagNamesToAdd.pop();
_.map(tagNamesToAdd, function(tagName) { callback(tagName); });
_.map(tagNamesToAdd, function(tagName) { addTag(tagName, source); });
$input.val(lastTagName);
}
function addTag(tagName) {
function addTag(tagName, source) {
tagName = tagName.trim();
if (tagName.length === 0) {
return;
@ -157,41 +173,55 @@ App.Controls.TagInput = function($underlyingInput) {
if (isTaggedWith(tagName)) {
flashTagRed(tagName);
} else {
beforeTagAdded(tagName);
beforeTagAdded(tagName, source);
var exportedTag = getExportedTag(tagName);
if (!exportedTag || !exportedTag.banned) {
addTagDirectly(tagName);
}
afterTagAdded(tagName);
}
}
function addTagDirectly(tagName) {
tags.push(tagName);
var $elem = createListElement(tagName);
$tagList.append($elem);
}
function beforeTagAdded(tagName) {
afterTagAdded(tagName, source);
}
}
function beforeTagRemoved(tagName) {
if (typeof(options.beforeTagRemoved) === 'function') {
options.beforeTagRemoved(tagName);
}
}
function afterTagRemoved(tagName) {
refreshShownSiblings();
}
function beforeTagAdded(tagName, source) {
if (typeof(options.beforeTagAdded) === 'function') {
options.beforeTagAdded(tagName);
}
}
function afterTagAdded(tagName) {
function afterTagAdded(tagName, source) {
if (source === SOURCE_IMPLICATIONS) {
flashTagYellow(tagName);
} else if (source !== SOURCE_INITIAL_TEXT) {
var tag = getExportedTag(tagName);
if (tag) {
_.each(tag.implications, function(impliedTagName) {
addTag(impliedTagName);
flashTagYellow(impliedTagName);
if (!isTaggedWith(impliedTagName)) {
addTag(impliedTagName, SOURCE_IMPLICATIONS);
}
});
showOrHideSuggestions(tag.suggestions);
if (source !== SOURCE_IMPLICATIONS && source !== SOURCE_SUGGESTIONS) {
showOrHideSuggestions(tagName);
refreshShownSiblings();
}
} else {
flashTagGreen(tagName);
}
}
}
function getExportedTag(tagName) {
return _.first(_.filter(
@ -205,10 +235,9 @@ App.Controls.TagInput = function($underlyingInput) {
var oldTagNames = getTags();
var newTagNames = _.without(oldTagNames, tagName);
if (newTagNames.length !== oldTagNames.length) {
if (typeof(options.beforeTagRemoved) === 'function') {
options.beforeTagRemoved(tagName);
}
beforeTagRemoved(tagName);
setTags(newTagNames);
afterTagRemoved(tagName);
}
}
@ -262,7 +291,8 @@ App.Controls.TagInput = function($underlyingInput) {
$tagLink.text(tagName);
$tagLink.click(function(e) {
e.preventDefault();
showOrHideTagSiblings(tagName);
showOrHideSiblings(tagName);
showOrHideSuggestions(tagName);
});
$elem.append($tagLink);
@ -276,19 +306,13 @@ App.Controls.TagInput = function($underlyingInput) {
return $elem;
}
function showOrHideSuggestions(suggestedTagNames) {
if (_.size(suggestedTagNames) === 0) {
return;
function showOrHideSuggestions(tagName) {
var tag = getExportedTag(tagName);
var suggestions = tag ? tag.suggestions : [];
updateSuggestions($suggestions, suggestions);
}
var suggestions = filterSuggestions(suggestedTagNames);
if (suggestions.length > 0) {
attachTagsToSuggestionList($suggestions.find('ul'), suggestions);
$suggestions.slideDown('fast');
}
}
function showOrHideTagSiblings(tagName) {
function showOrHideSiblings(tagName) {
if ($siblings.data('lastTag') === tagName && $siblings.is(':visible')) {
$siblings.slideUp('fast');
$siblings.data('lastTag', null);
@ -298,22 +322,23 @@ App.Controls.TagInput = function($underlyingInput) {
promise.wait(getSiblings(tagName), promise.make(function(resolve, reject) {
$siblings.slideUp('fast', resolve);
})).then(function(siblings) {
siblings = _.pluck(siblings, 'name');
$siblings.data('lastTag', tagName);
if (!_.size(siblings)) {
return;
}
var suggestions = filterSuggestions(_.pluck(siblings, 'name'));
if (suggestions.length > 0) {
attachTagsToSuggestionList($siblings.find('ul'), suggestions);
$siblings.slideDown('fast');
}
$siblings.data('siblings', siblings);
updateSuggestions($siblings, siblings);
}).fail(function() {
});
}
function refreshShownSiblings() {
updateSuggestions($siblings, $siblings.data('siblings'));
}
function updateSuggestions($target, suggestedTagNames) {
function filterSuggestions(sourceTagNames) {
if (!sourceTagNames) {
return [];
}
var tagNames = _.filter(sourceTagNames.slice(), function(tagName) {
return !isTaggedWith(tagName);
});
@ -329,7 +354,7 @@ App.Controls.TagInput = function($underlyingInput) {
$a.text(tagName);
$a.click(function(e) {
e.preventDefault();
addTag(tagName);
addTag(tagName, SOURCE_SUGGESTIONS);
$li.fadeOut('fast', function() {
$li.remove();
if ($list.children().length === 0) {
@ -342,6 +367,15 @@ App.Controls.TagInput = function($underlyingInput) {
});
}
var suggestions = filterSuggestions(suggestedTagNames);
if (suggestions.length > 0) {
attachTagsToSuggestionList($target.find('ul'), suggestions);
$target.slideDown('fast');
} else {
$target.slideUp('fast');
}
}
function getSiblings(tagName) {
return promise.make(function(resolve, reject) {
promise.wait(api.get('/tags/' + tagName + '/siblings'))
@ -364,6 +398,7 @@ App.Controls.TagInput = function($underlyingInput) {
function hideSuggestions() {
$siblings.hide();
$suggestions.hide();
$siblings.data('siblings', []);
}
_.extend(options, {

View File

@ -10,8 +10,6 @@ App.Presenters.TagListPresenter = function(
pagerPresenter,
topNavigationPresenter) {
var KEY_RETURN = 13;
var $el = jQuery('#content');
var $searchInput;
var templates = {};
@ -78,8 +76,8 @@ App.Presenters.TagListPresenter = function(
function render() {
$el.html(templates.list());
$searchInput = $el.find('input[name=query]');
$searchInput.keydown(searchInputKeyPressed);
$el.find('form').submit(searchFormSubmitted);
App.Controls.AutoCompleteInput($searchInput);
softRender();
}
@ -88,13 +86,6 @@ App.Presenters.TagListPresenter = function(
}
function searchInputKeyPressed(e) {
if (e.which !== KEY_RETURN) {
return;
}
updateSearch();
}
function searchFormSubmitted(e) {
e.preventDefault();
updateSearch();

View File

@ -128,6 +128,7 @@ App.Presenters.TagPresenter = function(
promise.wait(api.put('/tags/' + tag.name, formData))
.then(function(response) {
router.navigateInplace('#/tag/' + response.json.name);
tagList.refreshTags();
}).fail(function(response) {
window.alert(response.json && response.json.error || 'An error occured.');
});
@ -140,6 +141,7 @@ App.Presenters.TagPresenter = function(
promise.wait(api.delete('/tags/' + tag.name))
.then(function(response) {
router.navigate('#/tags');
tagList.refreshTags();
}).fail(function(response) {
window.alert(response.json && response.json.error || 'An error occured.');
});
@ -151,6 +153,7 @@ App.Presenters.TagPresenter = function(
promise.wait(api.put('/tags/' + tag.name + '/merge', {targetTag: targetTag}))
.then(function(response) {
router.navigate('#/tags');
tagList.refreshTags();
}).fail(function(response) {
window.alert(response.json && response.json.error || 'An error occured.');
});
@ -183,4 +186,16 @@ App.Presenters.TagPresenter = function(
};
App.DI.register('tagPresenter', ['_', 'jQuery', 'util', 'promise', 'auth', 'api', 'tagList', 'router', 'keyboard', 'topNavigationPresenter', 'messagePresenter'], App.Presenters.TagPresenter);
App.DI.register('tagPresenter', [
'_',
'jQuery',
'util',
'promise',
'auth',
'api',
'tagList',
'router',
'keyboard',
'topNavigationPresenter',
'messagePresenter'],
App.Presenters.TagPresenter);

View File

@ -210,6 +210,8 @@ App.Util.Misc = function(_, jQuery, marked, promise) {
text = text.replace(/\[search\]((?:[^\[]|\[(?!\/?search\]))+)\[\/search\]/ig, '<a href="#/posts/query=$1"><code>$1</code></a>');
//spoilers
text = text.replace(/\[spoiler\]((?:[^\[]|\[(?!\/?spoiler\]))+)\[\/spoiler\]/ig, '<span class="spoiler">$1</span>');
//[small]
text = text.replace(/\[small\]((?:[^\[]|\[(?!\/?small\]))+)\[\/small\]/ig, '<small>$1</small>');
//strike-through
text = text.replace(/(^|[^\\])(~~|~)([^~]+)\2/g, '$1<del>$3</del>');
text = text.replace(/\\~/g, '~');

View File

@ -1,7 +1,7 @@
<div id="home">
<h1><%= title %></h1>
<p>
<small>Serving <%= globals.postCount || 0 %> posts (<%= formatFileSize(globals.postSize || 0) %>)</small>
<p class="subheader">
Serving <%= globals.postCount || 0 %> posts (<%= formatFileSize(globals.postSize || 0) %>)
</p>
<% if (post && post.id) { %>
@ -11,7 +11,7 @@
<div class="post-footer">
<small class="left">
<span class="left">
<% var showLink = canViewPosts %>
<% if (showLink) { %>
@ -26,9 +26,9 @@
uploaded
<%= formatRelativeTime(post.uploadTime) %>
</small>
</span>
<small class="right">
<span class="right">
featured
<%= formatRelativeTime(post.lastFeatureTime) %>
by
@ -48,7 +48,7 @@
<% if (showLink) { %>
</a>
<% } %>
</small>
</span>
</div>
</div>

View File

@ -109,10 +109,13 @@ class PostService
{
$formData->validate($this->validator);
if ($formData->anonymous)
$this->authService->loginAnonymous();
$post = new Post();
$post->setUploadTime($this->timeService->getCurrentTime());
$post->setLastEditTime($this->timeService->getCurrentTime());
$post->setUser($formData->anonymous ? null : $this->authService->getLoggedInUser());
$post->setUser($this->authService->getLoggedInUser());
$post->setOriginalFileName($formData->contentFileName);
$post->setName($this->getUniqueRandomPostName());