mirror of
https://github.com/rr-/szurubooru.git
synced 2025-07-17 08:26:24 +00:00
remove tags.json
This commit is contained in:
@ -62,15 +62,16 @@ class PostListController {
|
||||
}
|
||||
|
||||
_evtTag(e) {
|
||||
for (let tag of this._bulkEditTags) {
|
||||
e.detail.post.addTag(tag);
|
||||
}
|
||||
e.detail.post.save().catch(error => window.alert(error.message));
|
||||
Promise.all(
|
||||
this._bulkEditTags.map(tag =>
|
||||
e.detail.post.tags.addByName(tag)))
|
||||
.then(() => { e.detail.post.save(); })
|
||||
.catch(error => window.alert(error.message));
|
||||
}
|
||||
|
||||
_evtUntag(e) {
|
||||
for (let tag of this._bulkEditTags) {
|
||||
e.detail.post.removeTag(tag);
|
||||
e.detail.post.tags.removeByName(tag);
|
||||
}
|
||||
e.detail.post.save().catch(error => window.alert(error.message));
|
||||
}
|
||||
|
@ -132,9 +132,6 @@ class PostMainController extends BasePostController {
|
||||
this._view.sidebarControl.disableForm();
|
||||
this._view.sidebarControl.clearMessages();
|
||||
const post = e.detail.post;
|
||||
if (e.detail.tags !== undefined) {
|
||||
post.tags = e.detail.tags;
|
||||
}
|
||||
if (e.detail.safety !== undefined) {
|
||||
post.safety = e.detail.safety;
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ const misc = require('../util/misc.js');
|
||||
const progress = require('../util/progress.js');
|
||||
const topNavigation = require('../models/top_navigation.js');
|
||||
const Post = require('../models/post.js');
|
||||
const Tag = require('../models/tag.js');
|
||||
const PostUploadView = require('../views/post_upload_view.js');
|
||||
const EmptyView = require('../views/empty_view.js');
|
||||
|
||||
@ -144,7 +145,11 @@ class PostUploadController {
|
||||
let post = new Post();
|
||||
post.safety = uploadable.safety;
|
||||
post.flags = uploadable.flags;
|
||||
post.tags = uploadable.tags;
|
||||
for (let tagName of uploadable.tags) {
|
||||
const tag = new Tag();
|
||||
tag.names = [tagName];
|
||||
post.tags.add(tag);
|
||||
}
|
||||
post.relations = uploadable.relations;
|
||||
post.newContent = uploadable.url || uploadable.file;
|
||||
return post;
|
||||
|
@ -40,7 +40,7 @@ class TagCategoriesController {
|
||||
this._view.disableForm();
|
||||
this._tagCategories.save()
|
||||
.then(() => {
|
||||
tags.refreshExport();
|
||||
tags.refreshCategoryColorMap();
|
||||
this._view.enableForm();
|
||||
this._view.showSuccess('Changes saved.');
|
||||
}, error => {
|
||||
|
@ -4,8 +4,8 @@ const router = require('../router.js');
|
||||
const api = require('../api.js');
|
||||
const misc = require('../util/misc.js');
|
||||
const uri = require('../util/uri.js');
|
||||
const tags = require('../tags.js');
|
||||
const Tag = require('../models/tag.js');
|
||||
const TagCategoryList = require('../models/tag_category_list.js');
|
||||
const topNavigation = require('../models/top_navigation.js');
|
||||
const TagView = require('../views/tag_view.js');
|
||||
const EmptyView = require('../views/empty_view.js');
|
||||
@ -18,7 +18,12 @@ class TagController {
|
||||
return;
|
||||
}
|
||||
|
||||
Tag.get(ctx.parameters.name).then(tag => {
|
||||
Promise.all([
|
||||
TagCategoryList.get(),
|
||||
Tag.get(ctx.parameters.name),
|
||||
]).then(responses => {
|
||||
const [tagCategoriesResponse, tag] = responses;
|
||||
|
||||
topNavigation.activate('tags');
|
||||
topNavigation.setTitle('Tag #' + tag.names[0]);
|
||||
|
||||
@ -26,7 +31,7 @@ class TagController {
|
||||
tag.addEventListener('change', e => this._evtSaved(e, section));
|
||||
|
||||
const categories = {};
|
||||
for (let category of tags.getAllCategories()) {
|
||||
for (let category of tagCategoriesResponse.results) {
|
||||
categories[category.name] = category.name;
|
||||
}
|
||||
|
||||
@ -76,12 +81,6 @@ class TagController {
|
||||
if (e.detail.category !== undefined) {
|
||||
e.detail.tag.category = e.detail.category;
|
||||
}
|
||||
if (e.detail.implications !== undefined) {
|
||||
e.detail.tag.implications = e.detail.implications;
|
||||
}
|
||||
if (e.detail.suggestions !== undefined) {
|
||||
e.detail.tag.suggestions = e.detail.suggestions;
|
||||
}
|
||||
if (e.detail.description !== undefined) {
|
||||
e.detail.tag.description = e.detail.description;
|
||||
}
|
||||
|
@ -11,7 +11,12 @@ const TagsPageView = require('../views/tags_page_view.js');
|
||||
const EmptyView = require('../views/empty_view.js');
|
||||
|
||||
const fields = [
|
||||
'names', 'suggestions', 'implications', 'creationTime', 'usages'];
|
||||
'names',
|
||||
'suggestions',
|
||||
'implications',
|
||||
'creationTime',
|
||||
'usages',
|
||||
'category'];
|
||||
|
||||
class TagListController {
|
||||
constructor(ctx) {
|
||||
|
@ -28,10 +28,7 @@ class AutoCompleteControl {
|
||||
this._sourceInputNode = sourceInputNode;
|
||||
this._options = {};
|
||||
Object.assign(this._options, {
|
||||
transform: null,
|
||||
verticalShift: 2,
|
||||
source: null,
|
||||
addSpace: false,
|
||||
maxResults: 15,
|
||||
getTextToFind: () => {
|
||||
const value = sourceInputNode.value;
|
||||
@ -56,7 +53,7 @@ class AutoCompleteControl {
|
||||
this._isVisible = false;
|
||||
}
|
||||
|
||||
defaultConfirmStrategy(text) {
|
||||
replaceSelectedText(result, addSpace) {
|
||||
const start = _getSelectionStart(this._sourceInputNode);
|
||||
let prefix = '';
|
||||
let suffix = this._sourceInputNode.value.substring(start);
|
||||
@ -66,30 +63,25 @@ class AutoCompleteControl {
|
||||
prefix = this._sourceInputNode.value.substring(0, index + 1);
|
||||
middle = this._sourceInputNode.value.substring(index + 1);
|
||||
}
|
||||
this._sourceInputNode.value = prefix + text + ' ' + suffix.trimLeft();
|
||||
if (!this._options.addSpace) {
|
||||
this._sourceInputNode.value = (
|
||||
prefix + result.toString() + ' ' + suffix.trimLeft());
|
||||
if (!addSpace) {
|
||||
this._sourceInputNode.value = this._sourceInputNode.value.trim();
|
||||
}
|
||||
this._sourceInputNode.focus();
|
||||
}
|
||||
|
||||
_delete(text) {
|
||||
if (this._options.transform) {
|
||||
text = this._options.transform(text);
|
||||
}
|
||||
_delete(result) {
|
||||
if (this._options.delete) {
|
||||
this._options.delete(text);
|
||||
this._options.delete(result);
|
||||
}
|
||||
}
|
||||
|
||||
_confirm(text) {
|
||||
if (this._options.transform) {
|
||||
text = this._options.transform(text);
|
||||
}
|
||||
_confirm(result) {
|
||||
if (this._options.confirm) {
|
||||
this._options.confirm(text);
|
||||
this._options.confirm(result);
|
||||
} else {
|
||||
this.defaultConfirmStrategy(text);
|
||||
this.defaultConfirmStrategy(result);
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,7 +96,6 @@ class AutoCompleteControl {
|
||||
this.hide();
|
||||
} else {
|
||||
this._updateResults(textToFind);
|
||||
this._refreshList();
|
||||
}
|
||||
}
|
||||
|
||||
@ -209,15 +200,16 @@ class AutoCompleteControl {
|
||||
}
|
||||
|
||||
_updateResults(textToFind) {
|
||||
const oldResults = this._results.slice();
|
||||
this._results =
|
||||
this._options.getMatches(textToFind)
|
||||
.slice(0, this._options.maxResults);
|
||||
const oldResultsHash = JSON.stringify(oldResults);
|
||||
const newResultsHash = JSON.stringify(this._results);
|
||||
if (oldResultsHash !== newResultsHash) {
|
||||
this._activeResult = -1;
|
||||
}
|
||||
this._options.getMatches(textToFind).then(matches => {
|
||||
const oldResults = this._results.slice();
|
||||
this._results = matches.slice(0, this._options.maxResults);
|
||||
const oldResultsHash = JSON.stringify(oldResults);
|
||||
const newResultsHash = JSON.stringify(this._results);
|
||||
if (oldResultsHash !== newResultsHash) {
|
||||
this._activeResult = -1;
|
||||
}
|
||||
this._refreshList();
|
||||
});
|
||||
}
|
||||
|
||||
_refreshList() {
|
||||
|
@ -72,7 +72,8 @@ class PostEditSidebarControl extends events.EventTarget {
|
||||
}
|
||||
|
||||
if (this._tagInputNode) {
|
||||
this._tagControl = new TagInputControl(this._tagInputNode);
|
||||
this._tagControl = new TagInputControl(
|
||||
this._tagInputNode, post.tags);
|
||||
}
|
||||
|
||||
if (this._contentInputNode) {
|
||||
@ -171,10 +172,11 @@ class PostEditSidebarControl extends events.EventTarget {
|
||||
});
|
||||
}
|
||||
|
||||
this._tagControl.addEventListener('change', e => {
|
||||
this._post.tags = this._tagControl.tags;
|
||||
this._syncExpanderTitles();
|
||||
});
|
||||
this._tagControl.addEventListener(
|
||||
'change', e => {
|
||||
this.dispatchEvent(new CustomEvent('change'));
|
||||
this._syncExpanderTitles();
|
||||
});
|
||||
|
||||
if (this._noteTextareaNode) {
|
||||
this._noteTextareaNode.addEventListener(
|
||||
|
@ -3,7 +3,6 @@
|
||||
const api = require('../api.js');
|
||||
const config = require('../config.js');
|
||||
const events = require('../events.js');
|
||||
const tags = require('../tags.js');
|
||||
const views = require('../util/views.js');
|
||||
|
||||
const template = views.getTemplate('post-readonly-sidebar');
|
||||
@ -22,8 +21,6 @@ class PostReadonlySidebarControl extends events.EventTarget {
|
||||
|
||||
views.replaceContent(this._hostNode, template({
|
||||
post: this._post,
|
||||
getTagCategory: this._getTagCategory,
|
||||
getTagUsages: this._getTagUsages,
|
||||
enableSafety: config.enableSafety,
|
||||
canListPosts: api.hasPrivilege('posts:list'),
|
||||
canEditPosts: api.hasPrivilege('posts:edit'),
|
||||
@ -161,16 +158,6 @@ class PostReadonlySidebarControl extends events.EventTarget {
|
||||
newNode.classList.add('active');
|
||||
}
|
||||
|
||||
_getTagUsages(name) {
|
||||
const tag = tags.getTagByName(name);
|
||||
return tag ? tag.usages : 0;
|
||||
}
|
||||
|
||||
_getTagCategory(name) {
|
||||
const tag = tags.getTagByName(name);
|
||||
return tag ? tag.category : 'unknown';
|
||||
}
|
||||
|
||||
_evtAddToFavoritesClick(e) {
|
||||
e.preventDefault();
|
||||
this.dispatchEvent(new CustomEvent('favorite', {
|
||||
|
@ -1,9 +1,33 @@
|
||||
'use strict';
|
||||
|
||||
const tags = require('../tags.js');
|
||||
const misc = require('../util/misc.js');
|
||||
const views = require('../util/views.js');
|
||||
const TagList = require('../models/tag_list.js');
|
||||
const AutoCompleteControl = require('./auto_complete_control.js');
|
||||
|
||||
function _escapeSearch(text) {
|
||||
return text.replace('\\', '\\\\').replace(':', '\\:');
|
||||
}
|
||||
|
||||
function _tagListToMatches(tags, options) {
|
||||
return [...tags].sort((tag1, tag2) => {
|
||||
return tag2.usages - tag1.usages;
|
||||
}).map(tag => {
|
||||
let cssName = misc.makeCssName(tag.category, 'tag');
|
||||
if (options.isTaggedWith(tag.names[0])) {
|
||||
cssName += ' disabled';
|
||||
}
|
||||
const caption = (
|
||||
'<span class="' + cssName + '">'
|
||||
+ misc.escapeHtml(tag.names[0] + ' (' + tag.postCount + ')')
|
||||
+ '</span>');
|
||||
return {
|
||||
caption: caption,
|
||||
value: tag,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
class TagAutoCompleteControl extends AutoCompleteControl {
|
||||
constructor(input, options) {
|
||||
const minLengthForPartialSearch = 3;
|
||||
@ -13,32 +37,21 @@ class TagAutoCompleteControl extends AutoCompleteControl {
|
||||
}, options);
|
||||
|
||||
options.getMatches = text => {
|
||||
const transform = x => x.toLowerCase();
|
||||
const match = text.length < minLengthForPartialSearch ?
|
||||
(a, b) => a.startsWith(b) :
|
||||
(a, b) => a.includes(b);
|
||||
text = transform(text);
|
||||
return Array.from(tags.getNameToTagMap().entries())
|
||||
.filter(kv => match(transform(kv[0]), text))
|
||||
.sort((kv1, kv2) => {
|
||||
return kv2[1].usages - kv1[1].usages;
|
||||
})
|
||||
.map(kv => {
|
||||
const origName = tags.getOriginalTagName(kv[0]);
|
||||
const category = kv[1].category;
|
||||
const usages = kv[1].usages;
|
||||
let cssName = misc.makeCssName(category, 'tag');
|
||||
if (options.isTaggedWith(kv[0])) {
|
||||
cssName += ' disabled';
|
||||
}
|
||||
return {
|
||||
caption: misc.unindent`
|
||||
<span class="${cssName}">
|
||||
${misc.escapeHtml(origName)} (${usages})
|
||||
</span>`,
|
||||
value: origName,
|
||||
};
|
||||
});
|
||||
const term = misc.escapeSearchTerm(text);
|
||||
const query = (
|
||||
text.length < minLengthForPartialSearch
|
||||
? term + '*'
|
||||
: '*' + term + '*') + ' sort:usages';
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
TagList.search(
|
||||
query, 0, this._options.maxResults,
|
||||
['names', 'category', 'usages'])
|
||||
.then(
|
||||
response => resolve(
|
||||
_tagListToMatches(response.results, this._options)),
|
||||
reject);
|
||||
});
|
||||
};
|
||||
|
||||
super(input, options);
|
||||
|
@ -4,6 +4,7 @@ const api = require('../api.js');
|
||||
const tags = require('../tags.js');
|
||||
const misc = require('../util/misc.js');
|
||||
const uri = require('../util/uri.js');
|
||||
const Tag = require('../models/tag.js');
|
||||
const settings = require('../models/settings.js');
|
||||
const events = require('../events.js');
|
||||
const views = require('../util/views.js');
|
||||
@ -80,11 +81,12 @@ class SuggestionList {
|
||||
}
|
||||
|
||||
class TagInputControl extends events.EventTarget {
|
||||
constructor(hostNode) {
|
||||
constructor(hostNode, tagList) {
|
||||
super();
|
||||
this.tags = [];
|
||||
this.tags = tagList;
|
||||
this._hostNode = hostNode;
|
||||
this._suggestions = new SuggestionList();
|
||||
this._tagToListItemNode = new Map();
|
||||
|
||||
// dom
|
||||
const editAreaNode = template();
|
||||
@ -98,16 +100,18 @@ class TagInputControl extends events.EventTarget {
|
||||
getTextToFind: () => {
|
||||
return this._tagInputNode.value;
|
||||
},
|
||||
confirm: text => {
|
||||
confirm: tag => {
|
||||
this._tagInputNode.value = '';
|
||||
this.addTag(text, SOURCE_USER_INPUT);
|
||||
// XXX: tags from autocomplete don't contain implications
|
||||
// so they need to be looked up in API
|
||||
this.addTagByName(tag.names[0], SOURCE_USER_INPUT);
|
||||
},
|
||||
delete: text => {
|
||||
delete: tag => {
|
||||
this._tagInputNode.value = '';
|
||||
this.deleteTag(text);
|
||||
this.deleteTag(tag);
|
||||
},
|
||||
verticalShift: -2,
|
||||
isTaggedWith: tagName => this.isTaggedWith(tagName),
|
||||
isTaggedWith: tagName => this.tags.isTaggedWith(tagName),
|
||||
});
|
||||
|
||||
// dom events
|
||||
@ -127,114 +131,81 @@ class TagInputControl extends events.EventTarget {
|
||||
this._hostNode.parentNode.insertBefore(
|
||||
this._editAreaNode, hostNode.nextSibling);
|
||||
|
||||
this.addEventListener('change', e => this._evtTagsChanged(e));
|
||||
this.addEventListener('add', e => this._evtTagAdded(e));
|
||||
this.addEventListener('remove', e => this._evtTagRemoved(e));
|
||||
|
||||
// add existing tags
|
||||
this.addMultipleTags(this._hostNode.value, SOURCE_INIT);
|
||||
for (let tag of [...this.tags]) {
|
||||
const listItemNode = this._createListItemNode(tag);
|
||||
this._tagListNode.appendChild(listItemNode);
|
||||
}
|
||||
}
|
||||
|
||||
isTaggedWith(tagName) {
|
||||
let actualTag = null;
|
||||
[tagName, actualTag] = this._transformTagName(tagName);
|
||||
return this.tags
|
||||
.map(t => t.toLowerCase())
|
||||
.includes(tagName.toLowerCase());
|
||||
}
|
||||
|
||||
addMultipleTags(text, source) {
|
||||
addTagByText(text, source) {
|
||||
for (let tagName of text.split(/\s+/).filter(word => word).reverse()) {
|
||||
this.addTag(tagName, source);
|
||||
this.addTagByName(tagName, source);
|
||||
}
|
||||
}
|
||||
|
||||
addTag(tagName, source) {
|
||||
tagName = tags.getOriginalTagName(tagName);
|
||||
|
||||
if (!tagName) {
|
||||
addTagByName(name, source) {
|
||||
name = name.trim();
|
||||
if (!name) {
|
||||
return;
|
||||
}
|
||||
|
||||
let actualTag = null;
|
||||
[tagName, actualTag] = this._transformTagName(tagName);
|
||||
if (!this.isTaggedWith(tagName)) {
|
||||
this.tags.push(tagName);
|
||||
}
|
||||
this.dispatchEvent(new CustomEvent('add', {
|
||||
detail: {
|
||||
tagName: tagName,
|
||||
source: source,
|
||||
},
|
||||
}));
|
||||
this.dispatchEvent(new CustomEvent('change'));
|
||||
|
||||
// XXX: perhaps we should aggregate suggestions from all implications
|
||||
// for call to the _suggestRelations
|
||||
if (source !== SOURCE_INIT && source !== SOURCE_CLIPBOARD) {
|
||||
for (let otherTagName of tags.getAllImplications(tagName)) {
|
||||
this.addTag(otherTagName, SOURCE_IMPLICATION);
|
||||
}
|
||||
}
|
||||
return Tag.get(name).then(tag => {
|
||||
return this.addTag(tag, source);
|
||||
}, () => {
|
||||
const tag = new Tag();
|
||||
tag.names = [name];
|
||||
tag.category = null;
|
||||
return this.addTag(tag, source);
|
||||
});
|
||||
}
|
||||
|
||||
deleteTag(tagName) {
|
||||
if (!tagName) {
|
||||
return;
|
||||
}
|
||||
let actualTag = null;
|
||||
[tagName, actualTag] = this._transformTagName(tagName);
|
||||
if (!this.isTaggedWith(tagName)) {
|
||||
return;
|
||||
}
|
||||
this._hideAutoComplete();
|
||||
this.tags = this.tags.filter(
|
||||
t => t.toLowerCase() != tagName.toLowerCase());
|
||||
this.dispatchEvent(new CustomEvent('remove', {
|
||||
detail: {
|
||||
tagName: tagName,
|
||||
},
|
||||
}));
|
||||
this.dispatchEvent(new CustomEvent('change'));
|
||||
}
|
||||
|
||||
_evtTagsChanged(e) {
|
||||
this._hostNode.value = this.tags.join(' ');
|
||||
this._hostNode.dispatchEvent(new CustomEvent('change'));
|
||||
}
|
||||
|
||||
_evtTagAdded(e) {
|
||||
const tagName = e.detail.tagName;
|
||||
const actualTag = tags.getTagByName(tagName);
|
||||
let listItemNode = this._getListItemNodeFromTagName(tagName);
|
||||
const alreadyAdded = !!listItemNode;
|
||||
if (alreadyAdded) {
|
||||
if (e.detail.source !== SOURCE_IMPLICATION) {
|
||||
addTag(tag, source) {
|
||||
if (source != SOURCE_INIT && this.tags.isTaggedWith(tag.names[0])) {
|
||||
const listItemNode = this._getListItemNode(tag);
|
||||
if (source !== SOURCE_IMPLICATION) {
|
||||
listItemNode.classList.add('duplicate');
|
||||
_fadeOutListItemNodeStatus(listItemNode);
|
||||
}
|
||||
} else {
|
||||
listItemNode = this._createListItemNode(tagName);
|
||||
if (!actualTag) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return this.tags.addByName(tag.names[0], false).then(() => {
|
||||
const listItemNode = this._createListItemNode(tag);
|
||||
if (!tag.category) {
|
||||
listItemNode.classList.add('new');
|
||||
}
|
||||
if (e.detail.source === SOURCE_IMPLICATION) {
|
||||
if (source === SOURCE_IMPLICATION) {
|
||||
listItemNode.classList.add('implication');
|
||||
}
|
||||
this._tagListNode.prependChild(listItemNode);
|
||||
}
|
||||
_fadeOutListItemNodeStatus(listItemNode);
|
||||
_fadeOutListItemNodeStatus(listItemNode);
|
||||
|
||||
if ([SOURCE_USER_INPUT, SOURCE_SUGGESTION].includes(e.detail.source) &&
|
||||
actualTag) {
|
||||
this._loadSuggestions(actualTag);
|
||||
}
|
||||
return Promise.all(
|
||||
tag.implications.map(
|
||||
implication => this.addTagByName(
|
||||
implication.names[0], SOURCE_IMPLICATION)));
|
||||
}).then(() => {
|
||||
this.dispatchEvent(new CustomEvent('add', {
|
||||
detail: {tag: tag, source: source},
|
||||
}));
|
||||
this.dispatchEvent(new CustomEvent('change'));
|
||||
return Promise.resolve();
|
||||
});
|
||||
}
|
||||
|
||||
_evtTagRemoved(e) {
|
||||
const listItemNode = this._getListItemNodeFromTagName(e.detail.tagName);
|
||||
if (listItemNode) {
|
||||
listItemNode.parentNode.removeChild(listItemNode);
|
||||
deleteTag(tag) {
|
||||
if (!this.tags.isTaggedWith(tag.names[0])) {
|
||||
return;
|
||||
}
|
||||
this.tags.removeByName(tag.names[0]);
|
||||
this._hideAutoComplete();
|
||||
|
||||
this._deleteListItemNode(tag);
|
||||
|
||||
this.dispatchEvent(new CustomEvent('remove', {
|
||||
detail: {tag: tag},
|
||||
}));
|
||||
this.dispatchEvent(new CustomEvent('change'));
|
||||
}
|
||||
|
||||
_evtInputPaste(e) {
|
||||
@ -248,7 +219,7 @@ class TagInputControl extends events.EventTarget {
|
||||
return;
|
||||
}
|
||||
this._hideAutoComplete();
|
||||
this.addMultipleTags(pastedText, SOURCE_CLIPBOARD);
|
||||
this.addTagByText(pastedText, SOURCE_CLIPBOARD);
|
||||
this._tagInputNode.value = '';
|
||||
}
|
||||
|
||||
@ -259,7 +230,7 @@ class TagInputControl extends events.EventTarget {
|
||||
|
||||
_evtAddTagButtonClick(e) {
|
||||
e.preventDefault();
|
||||
this.addTag(this._tagInputNode.value, SOURCE_USER_INPUT);
|
||||
this.addTagByName(this._tagInputNode.value, SOURCE_USER_INPUT);
|
||||
this._tagInputNode.value = '';
|
||||
}
|
||||
|
||||
@ -272,36 +243,14 @@ class TagInputControl extends events.EventTarget {
|
||||
if (e.which == KEY_RETURN || e.which == KEY_SPACE) {
|
||||
e.preventDefault();
|
||||
this._hideAutoComplete();
|
||||
this.addMultipleTags(this._tagInputNode.value, SOURCE_USER_INPUT);
|
||||
this.addTagByText(this._tagInputNode.value, SOURCE_USER_INPUT);
|
||||
this._tagInputNode.value = '';
|
||||
}
|
||||
}
|
||||
|
||||
_transformTagName(tagName) {
|
||||
const actualTag = tags.getTagByName(tagName);
|
||||
if (actualTag) {
|
||||
tagName = actualTag.names[0];
|
||||
}
|
||||
return [tagName, actualTag];
|
||||
}
|
||||
|
||||
_getListItemNodeFromTagName(tagName) {
|
||||
let actualTag = null;
|
||||
[tagName, actualTag] = this._transformTagName(tagName);
|
||||
for (let listItemNode of this._tagListNode.querySelectorAll('li')) {
|
||||
if (listItemNode.getAttribute('data-tag').toLowerCase() ===
|
||||
tagName.toLowerCase()) {
|
||||
return listItemNode;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
_createListItemNode(tagName) {
|
||||
let actualTag = null;
|
||||
[tagName, actualTag] = this._transformTagName(tagName);
|
||||
const className = actualTag ?
|
||||
misc.makeCssName(actualTag.category, 'tag') :
|
||||
_createListItemNode(tag) {
|
||||
const className = tag.category ?
|
||||
misc.makeCssName(tag.category, 'tag') :
|
||||
null;
|
||||
|
||||
const tagLinkNode = document.createElement('a');
|
||||
@ -309,7 +258,8 @@ class TagInputControl extends events.EventTarget {
|
||||
tagLinkNode.classList.add(className);
|
||||
}
|
||||
tagLinkNode.setAttribute(
|
||||
'href', uri.formatClientLink('tag', tagName));
|
||||
'href', uri.formatClientLink('tag', tag.names[0]));
|
||||
|
||||
const tagIconNode = document.createElement('i');
|
||||
tagIconNode.classList.add('fa');
|
||||
tagIconNode.classList.add('fa-tag');
|
||||
@ -320,13 +270,13 @@ class TagInputControl extends events.EventTarget {
|
||||
searchLinkNode.classList.add(className);
|
||||
}
|
||||
searchLinkNode.setAttribute(
|
||||
'href', uri.formatClientLink('posts', {query: tagName}));
|
||||
searchLinkNode.textContent = tagName + ' ';
|
||||
'href', uri.formatClientLink('posts', {query: tag.names[0]}));
|
||||
searchLinkNode.textContent = tag.names[0] + ' ';
|
||||
searchLinkNode.addEventListener('click', e => {
|
||||
e.preventDefault();
|
||||
if (actualTag) {
|
||||
this._suggestions.clear();
|
||||
this._loadSuggestions(actualTag);
|
||||
this._suggestions.clear();
|
||||
if (tag.postCount > 0) {
|
||||
this._loadSuggestions(tag);
|
||||
this._removeSuggestionsPopupOpacity();
|
||||
} else {
|
||||
this._closeSuggestionsPopup();
|
||||
@ -335,8 +285,7 @@ class TagInputControl extends events.EventTarget {
|
||||
|
||||
const usagesNode = document.createElement('span');
|
||||
usagesNode.classList.add('tag-usages');
|
||||
usagesNode.setAttribute(
|
||||
'data-pseudo-content', actualTag ? actualTag.usages : 0);
|
||||
usagesNode.setAttribute('data-pseudo-content', tag.postCount);
|
||||
|
||||
const removalLinkNode = document.createElement('a');
|
||||
removalLinkNode.classList.add('remove-tag');
|
||||
@ -344,18 +293,34 @@ class TagInputControl extends events.EventTarget {
|
||||
removalLinkNode.setAttribute('data-pseudo-content', '×');
|
||||
removalLinkNode.addEventListener('click', e => {
|
||||
e.preventDefault();
|
||||
this.deleteTag(tagName);
|
||||
this.deleteTag(tag);
|
||||
});
|
||||
|
||||
const listItemNode = document.createElement('li');
|
||||
listItemNode.setAttribute('data-tag', tagName);
|
||||
listItemNode.appendChild(removalLinkNode);
|
||||
listItemNode.appendChild(tagLinkNode);
|
||||
listItemNode.appendChild(searchLinkNode);
|
||||
listItemNode.appendChild(usagesNode);
|
||||
for (let name of tag.names) {
|
||||
this._tagToListItemNode.set(name, listItemNode);
|
||||
}
|
||||
return listItemNode;
|
||||
}
|
||||
|
||||
_deleteListItemNode(tag) {
|
||||
const listItemNode = this._getListItemNode(tag);
|
||||
if (listItemNode) {
|
||||
listItemNode.parentNode.removeChild(listItemNode);
|
||||
}
|
||||
for (let name of tag.names) {
|
||||
this._tagToListItemNode.delete(name);
|
||||
}
|
||||
}
|
||||
|
||||
_getListItemNode(tag) {
|
||||
return this._tagToListItemNode.get(tag.names[0]);
|
||||
}
|
||||
|
||||
_loadSuggestions(tag) {
|
||||
const browsingSettings = settings.get();
|
||||
if (!browsingSettings.tagSuggestions) {
|
||||
@ -399,23 +364,22 @@ class TagInputControl extends events.EventTarget {
|
||||
for (let tuple of this._suggestions.getAll()) {
|
||||
const tagName = tuple.tagName;
|
||||
const weight = tuple.weight;
|
||||
if (this.isTaggedWith(tagName)) {
|
||||
if (this.tags.isTaggedWith(tagName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const actualTag = tags.getTagByName(tagName);
|
||||
const addLinkNode = document.createElement('a');
|
||||
addLinkNode.textContent = tagName;
|
||||
addLinkNode.classList.add('add-tag');
|
||||
addLinkNode.setAttribute('href', '');
|
||||
if (actualTag) {
|
||||
Tag.get(tagName).then(tag => {
|
||||
addLinkNode.classList.add(
|
||||
misc.makeCssName(actualTag.category, 'tag'));
|
||||
}
|
||||
misc.makeCssName(tag.category, 'tag'));
|
||||
});
|
||||
addLinkNode.addEventListener('click', e => {
|
||||
e.preventDefault();
|
||||
listNode.removeChild(listItemNode);
|
||||
this.addTag(tagName, SOURCE_SUGGESTION);
|
||||
this.addTagByName(tagName, SOURCE_SUGGESTION);
|
||||
});
|
||||
|
||||
const weightNode = document.createElement('span');
|
||||
|
@ -55,7 +55,7 @@ for (let controller of controllers) {
|
||||
|
||||
const tags = require('./tags.js');
|
||||
const api = require('./api.js');
|
||||
tags.refreshExport(); // we don't care about errors
|
||||
tags.refreshCategoryColorMap(); // we don't care about errors
|
||||
api.loginFromCookies().then(() => {
|
||||
router.start();
|
||||
}, error => {
|
||||
|
@ -27,6 +27,13 @@ class AbstractList extends events.EventTarget {
|
||||
return ret;
|
||||
}
|
||||
|
||||
sync(plainList) {
|
||||
this.clear();
|
||||
for (let item of (plainList || [])) {
|
||||
this.add(this.constructor._itemClass.fromResponse(item));
|
||||
}
|
||||
}
|
||||
|
||||
add(item) {
|
||||
if (item.addEventListener) {
|
||||
item.addEventListener('delete', e => {
|
||||
@ -75,6 +82,10 @@ class AbstractList extends events.EventTarget {
|
||||
return this._list[index];
|
||||
}
|
||||
|
||||
map(...args) {
|
||||
return this._list.map(...args);
|
||||
}
|
||||
|
||||
[Symbol.iterator]() {
|
||||
return this._list[Symbol.iterator]();
|
||||
}
|
||||
|
@ -4,23 +4,18 @@ const api = require('../api.js');
|
||||
const uri = require('../util/uri.js');
|
||||
const tags = require('../tags.js');
|
||||
const events = require('../events.js');
|
||||
const TagList = require('./tag_list.js');
|
||||
const NoteList = require('./note_list.js');
|
||||
const CommentList = require('./comment_list.js');
|
||||
const misc = require('../util/misc.js');
|
||||
|
||||
function _syncObservableCollection(target, plainList) {
|
||||
target.clear();
|
||||
for (let item of (plainList || [])) {
|
||||
target.add(target.constructor._itemClass.fromResponse(item));
|
||||
}
|
||||
}
|
||||
|
||||
class Post extends events.EventTarget {
|
||||
constructor() {
|
||||
super();
|
||||
this._orig = {};
|
||||
|
||||
for (let obj of [this, this._orig]) {
|
||||
obj._tags = new TagList();
|
||||
obj._notes = new NoteList();
|
||||
obj._comments = new CommentList();
|
||||
}
|
||||
@ -56,7 +51,6 @@ class Post extends events.EventTarget {
|
||||
get hasCustomThumbnail() { return this._hasCustomThumbnail; }
|
||||
|
||||
set flags(value) { this._flags = value; }
|
||||
set tags(value) { this._tags = value; }
|
||||
set safety(value) { this._safety = value; }
|
||||
set relations(value) { this._relations = value; }
|
||||
set newContent(value) { this._newContent = value; }
|
||||
@ -94,29 +88,6 @@ class Post extends events.EventTarget {
|
||||
});
|
||||
}
|
||||
|
||||
isTaggedWith(tagName) {
|
||||
return this._tags
|
||||
.map(s => s.toLowerCase())
|
||||
.includes(tagName.toLowerCase());
|
||||
}
|
||||
|
||||
addTag(tagName, addImplications) {
|
||||
if (this.isTaggedWith(tagName)) {
|
||||
return;
|
||||
}
|
||||
this._tags.push(tagName);
|
||||
if (addImplications !== false) {
|
||||
for (let otherTag of tags.getAllImplications(tagName)) {
|
||||
this.addTag(otherTag, addImplications);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
removeTag(tagName) {
|
||||
this._tags = this._tags.filter(
|
||||
s => s.toLowerCase() != tagName.toLowerCase());
|
||||
}
|
||||
|
||||
save(anonymous) {
|
||||
const files = {};
|
||||
const detail = {version: this._version};
|
||||
@ -132,15 +103,14 @@ class Post extends events.EventTarget {
|
||||
detail.flags = this._flags;
|
||||
}
|
||||
if (misc.arraysDiffer(this._tags, this._orig._tags)) {
|
||||
detail.tags = this._tags;
|
||||
detail.tags = this._tags.map(tag => tag.names[0]);
|
||||
}
|
||||
if (misc.arraysDiffer(this._relations, this._orig._relations)) {
|
||||
detail.relations = this._relations;
|
||||
}
|
||||
if (misc.arraysDiffer(this._notes, this._orig._notes)) {
|
||||
detail.notes = [...this._notes].map(note => ({
|
||||
polygon: [...note.polygon].map(
|
||||
point => [point.x, point.y]),
|
||||
detail.notes = this._notes.map(note => ({
|
||||
polygon: note.polygon.map(point => [point.x, point.y]),
|
||||
text: note.text,
|
||||
}));
|
||||
}
|
||||
@ -310,7 +280,6 @@ class Post extends events.EventTarget {
|
||||
_fileSize: response.fileSize,
|
||||
|
||||
_flags: [...response.flags || []],
|
||||
_tags: [...response.tags || []],
|
||||
_relations: [...response.relations || []],
|
||||
|
||||
_score: response.score,
|
||||
@ -322,8 +291,9 @@ class Post extends events.EventTarget {
|
||||
});
|
||||
|
||||
for (let obj of [this, this._orig]) {
|
||||
_syncObservableCollection(obj._notes, response.notes);
|
||||
_syncObservableCollection(obj._comments, response.comments);
|
||||
obj._tags.sync(response.tags);
|
||||
obj._notes.sync(response.notes);
|
||||
obj._comments.sync(response.comments);
|
||||
}
|
||||
|
||||
Object.assign(this, map());
|
||||
|
@ -7,8 +7,16 @@ const misc = require('../util/misc.js');
|
||||
|
||||
class Tag extends events.EventTarget {
|
||||
constructor() {
|
||||
const TagList = require('./tag_list.js');
|
||||
|
||||
super();
|
||||
this._orig = {};
|
||||
|
||||
for (let obj of [this, this._orig]) {
|
||||
obj._suggestions = new TagList();
|
||||
obj._implications = new TagList();
|
||||
}
|
||||
|
||||
this._updateFromResponse({});
|
||||
}
|
||||
|
||||
@ -24,8 +32,6 @@ class Tag extends events.EventTarget {
|
||||
set names(value) { this._names = value; }
|
||||
set category(value) { this._category = value; }
|
||||
set description(value) { this._description = value; }
|
||||
set implications(value) { this._implications = value; }
|
||||
set suggestions(value) { this._suggestions = value; }
|
||||
|
||||
static fromResponse(response) {
|
||||
const ret = new Tag();
|
||||
@ -54,10 +60,12 @@ class Tag extends events.EventTarget {
|
||||
detail.description = this._description;
|
||||
}
|
||||
if (misc.arraysDiffer(this._implications, this._orig._implications)) {
|
||||
detail.implications = this._implications;
|
||||
detail.implications = this._implications.map(
|
||||
relation => relation.names[0]);
|
||||
}
|
||||
if (misc.arraysDiffer(this._suggestions, this._orig._suggestions)) {
|
||||
detail.suggestions = this._suggestions;
|
||||
detail.suggestions = this._suggestions.map(
|
||||
relation => relation.names[0]);
|
||||
}
|
||||
|
||||
let promise = this._origName ?
|
||||
@ -124,13 +132,16 @@ class Tag extends events.EventTarget {
|
||||
_names: response.names,
|
||||
_category: response.category,
|
||||
_description: response.description,
|
||||
_implications: response.implications,
|
||||
_suggestions: response.suggestions,
|
||||
_creationTime: response.creationTime,
|
||||
_lastEditTime: response.lastEditTime,
|
||||
_postCount: response.usages,
|
||||
_postCount: response.usages || 0,
|
||||
};
|
||||
|
||||
for (let obj of [this, this._orig]) {
|
||||
obj._suggestions.sync(response.suggestions);
|
||||
obj._implications.sync(response.implications);
|
||||
}
|
||||
|
||||
Object.assign(this, map);
|
||||
Object.assign(this._orig, map);
|
||||
}
|
||||
|
@ -22,6 +22,48 @@ class TagList extends AbstractList {
|
||||
{results: TagList.fromResponse(response.results)}));
|
||||
});
|
||||
}
|
||||
|
||||
isTaggedWith(testName) {
|
||||
for (let tag of this._list) {
|
||||
for (let tagName of tag.names) {
|
||||
if (tagName.toLowerCase() === testName.toLowerCase()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
addByName(tagName, addImplications) {
|
||||
if (this.isTaggedWith(tagName)) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const tag = new Tag();
|
||||
tag.names = [tagName];
|
||||
|
||||
this.add(tag);
|
||||
|
||||
if (addImplications !== false) {
|
||||
return Tag.get(tagName).then(actualTag => {
|
||||
return Promise.all(
|
||||
actualTag.implications.map(
|
||||
relation => this.addByName(relation.names[0], true)));
|
||||
});
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
removeByName(testName) {
|
||||
for (let tag of this._list) {
|
||||
for (let tagName of tag.names) {
|
||||
if (tagName.toLowerCase() === testName.toLowerCase()) {
|
||||
this.remove(tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TagList._itemClass = Tag;
|
||||
|
@ -1,92 +1,23 @@
|
||||
'use strict';
|
||||
|
||||
const misc = require('./util/misc.js');
|
||||
const request = require('superagent');
|
||||
const TagCategoryList = require('./models/tag_category_list.js');
|
||||
|
||||
let _tags = new Map();
|
||||
let _categories = new Map();
|
||||
let _stylesheet = null;
|
||||
|
||||
function getTagByName(name) {
|
||||
return _tags.get(name.toLowerCase());
|
||||
}
|
||||
|
||||
function getCategoryByName(name) {
|
||||
return _categories.get(name.toLowerCase());
|
||||
}
|
||||
|
||||
function getNameToTagMap() {
|
||||
return _tags;
|
||||
}
|
||||
|
||||
function getAllTags() {
|
||||
return _tags.values();
|
||||
}
|
||||
|
||||
function getAllCategories() {
|
||||
return _categories.values();
|
||||
}
|
||||
|
||||
function getOriginalTagName(name) {
|
||||
const actualTag = getTagByName(name);
|
||||
if (actualTag) {
|
||||
for (let originalName of actualTag.names) {
|
||||
if (originalName.toLowerCase() == name.toLowerCase()) {
|
||||
return originalName;
|
||||
}
|
||||
function refreshCategoryColorMap() {
|
||||
return TagCategoryList.get().then(response => {
|
||||
if (_stylesheet) {
|
||||
document.head.removeChild(_stylesheet);
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
function _tagsToMap(tags) {
|
||||
let map = new Map();
|
||||
for (let tag of tags) {
|
||||
for (let name of tag.names) {
|
||||
map.set(name.toLowerCase(), tag);
|
||||
_stylesheet = document.createElement('style');
|
||||
document.head.appendChild(_stylesheet);
|
||||
for (let category of response.results) {
|
||||
const ruleName = misc.makeCssName(category.name, 'tag');
|
||||
_stylesheet.sheet.insertRule(
|
||||
`.${ruleName} { color: ${category.color} }`,
|
||||
_stylesheet.sheet.cssRules.length);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
function _tagCategoriesToMap(categories) {
|
||||
let map = new Map();
|
||||
for (let category of categories) {
|
||||
map.set(category.name.toLowerCase(), category);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
function _refreshStylesheet() {
|
||||
if (_stylesheet) {
|
||||
document.head.removeChild(_stylesheet);
|
||||
}
|
||||
_stylesheet = document.createElement('style');
|
||||
document.head.appendChild(_stylesheet);
|
||||
for (let category of getAllCategories()) {
|
||||
const ruleName = misc.makeCssName(category.name, 'tag');
|
||||
_stylesheet.sheet.insertRule(
|
||||
`.${ruleName} { color: ${category.color} }`,
|
||||
_stylesheet.sheet.cssRules.length);
|
||||
}
|
||||
}
|
||||
|
||||
function refreshExport() {
|
||||
return new Promise((resolve, reject) => {
|
||||
request.get('/data/tags.json').end((error, response) => {
|
||||
if (error) {
|
||||
_tags = new Map();
|
||||
_categories = new Map();
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
_tags = _tagsToMap(
|
||||
response.body ? response.body.tags : []);
|
||||
_categories = _tagCategoriesToMap(
|
||||
response.body ? response.body.categories : []);
|
||||
_refreshStylesheet();
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -107,19 +38,7 @@ function getAllImplications(tagName) {
|
||||
return Array.from(implications);
|
||||
}
|
||||
|
||||
function getSuggestions(tagName) {
|
||||
const actualTag = getTagByName(tagName) || {};
|
||||
return actualTag.suggestions || [];
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getAllCategories: getAllCategories,
|
||||
getAllTags: getAllTags,
|
||||
getTagByName: getTagByName,
|
||||
getCategoryByName: getCategoryByName,
|
||||
getNameToTagMap: getNameToTagMap,
|
||||
getOriginalTagName: getOriginalTagName,
|
||||
refreshExport: refreshExport,
|
||||
getAllImplications: getAllImplications,
|
||||
getSuggestions: getSuggestions,
|
||||
refreshCategoryColorMap: refreshCategoryColorMap,
|
||||
getAllImplications: getAllImplications,
|
||||
};
|
||||
|
@ -3,7 +3,6 @@
|
||||
require('../util/polyfill.js');
|
||||
const api = require('../api.js');
|
||||
const templates = require('../templates.js');
|
||||
const tags = require('../tags.js');
|
||||
const domParser = new DOMParser();
|
||||
const misc = require('./misc.js');
|
||||
const uri = require('./uri.js');
|
||||
@ -194,13 +193,15 @@ function makePostLink(id, includeHash) {
|
||||
misc.escapeHtml(text);
|
||||
}
|
||||
|
||||
function makeTagLink(name, includeHash) {
|
||||
const tag = tags.getTagByName(name);
|
||||
function makeTagLink(name, includeHash, includeCount, tag) {
|
||||
const category = tag ? tag.category : 'unknown';
|
||||
let text = name;
|
||||
if (includeHash === true) {
|
||||
text = '#' + text;
|
||||
}
|
||||
if (includeCount === true) {
|
||||
text += ' (' + (tag ? tag.postCount : 0) + ')';
|
||||
}
|
||||
return api.hasPrivilege('tags:view') ?
|
||||
makeElement(
|
||||
'a',
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
const router = require('../router.js');
|
||||
const uri = require('../util/uri.js');
|
||||
const misc = require('../util/misc.js');
|
||||
const views = require('../util/views.js');
|
||||
const PostContentControl = require('../controls/post_content_control.js');
|
||||
const PostNotesOverlayControl
|
||||
@ -23,12 +24,16 @@ class HomeView {
|
||||
views.syncScrollPosition();
|
||||
|
||||
if (this._formNode) {
|
||||
this._tagAutoCompleteControl = new TagAutoCompleteControl(
|
||||
this._searchInputNode);
|
||||
this._autoCompleteControl = new TagAutoCompleteControl(
|
||||
this._searchInputNode,
|
||||
{
|
||||
confirm: tag =>
|
||||
this._autoCompleteControl.replaceSelectedText(
|
||||
misc.escapeSearchTerm(tag.names[0]), true),
|
||||
});
|
||||
this._formNode.addEventListener(
|
||||
'submit', e => this._evtFormSubmit(e));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
showSuccess(text) {
|
||||
|
@ -73,7 +73,12 @@ class BulkTagEditor extends BulkEditor {
|
||||
constructor(hostNode) {
|
||||
super(hostNode);
|
||||
this._autoCompleteControl = new TagAutoCompleteControl(
|
||||
this._inputNode, {addSpace: false});
|
||||
this._inputNode,
|
||||
{
|
||||
confirm: tag =>
|
||||
this._autoCompleteControl.replaceSelectedText(
|
||||
tag.names[0], false),
|
||||
});
|
||||
this._hostNode.addEventListener('submit', e => this._evtFormSubmit(e));
|
||||
}
|
||||
|
||||
@ -124,9 +129,13 @@ class PostsHeaderView extends events.EventTarget {
|
||||
this._hostNode = ctx.hostNode;
|
||||
views.replaceContent(this._hostNode, template(ctx));
|
||||
|
||||
this._queryAutoCompleteControl = new TagAutoCompleteControl(
|
||||
this._autoCompleteControl = new TagAutoCompleteControl(
|
||||
this._queryInputNode,
|
||||
{addSpace: true, transform: misc.escapeSearchTerm});
|
||||
{
|
||||
confirm: tag =>
|
||||
this._autoCompleteControl.replaceSelectedText(
|
||||
misc.escapeSearchTerm(tag.names[0]), true),
|
||||
});
|
||||
|
||||
keyboard.bind('p', () => this._focusFirstPostNode());
|
||||
search.searchInputNodeFocusHelper(this._queryInputNode);
|
||||
@ -235,7 +244,7 @@ class PostsHeaderView extends events.EventTarget {
|
||||
}
|
||||
|
||||
_navigate() {
|
||||
this._queryAutoCompleteControl.hide();
|
||||
this._autoCompleteControl.hide();
|
||||
let parameters = {query: this._queryInputNode.value};
|
||||
parameters.offset = parameters.query === this._ctx.parameters.query ?
|
||||
this._ctx.parameters.offset : 0;
|
||||
|
@ -100,7 +100,7 @@ class PostsPageView extends events.EventTarget {
|
||||
if (tagFlipperNode) {
|
||||
let tagged = true;
|
||||
for (let tag of this._ctx.bulkEdit.tags) {
|
||||
tagged = tagged & post.isTaggedWith(tag);
|
||||
tagged = tagged & post.tags.isTaggedWith(tag);
|
||||
}
|
||||
tagFlipperNode.classList.toggle('tagged', tagged);
|
||||
}
|
||||
|
@ -24,10 +24,12 @@ class TagEditView extends events.EventTarget {
|
||||
}
|
||||
|
||||
if (this._implicationsFieldNode) {
|
||||
new TagInputControl(this._implicationsFieldNode);
|
||||
new TagInputControl(
|
||||
this._implicationsFieldNode, this._tag.implications);
|
||||
}
|
||||
if (this._suggestionsFieldNode) {
|
||||
new TagInputControl(this._suggestionsFieldNode);
|
||||
new TagInputControl(
|
||||
this._suggestionsFieldNode, this._tag.suggestions);
|
||||
}
|
||||
|
||||
for (let node of this._formNode.querySelectorAll(
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
const config = require('../config.js');
|
||||
const events = require('../events.js');
|
||||
const misc = require('../util/misc.js');
|
||||
const views = require('../util/views.js');
|
||||
const TagAutoCompleteControl =
|
||||
require('../controls/tag_auto_complete_control.js');
|
||||
@ -19,7 +20,13 @@ class TagMergeView extends events.EventTarget {
|
||||
|
||||
views.decorateValidator(this._formNode);
|
||||
if (this._targetTagFieldNode) {
|
||||
new TagAutoCompleteControl(this._targetTagFieldNode);
|
||||
this._autoCompleteControl = new TagAutoCompleteControl(
|
||||
this._targetTagFieldNode,
|
||||
{
|
||||
confirm: tag =>
|
||||
this._autoCompleteControl.replaceSelectedText(
|
||||
tag.names[0], false),
|
||||
});
|
||||
}
|
||||
|
||||
this._formNode.addEventListener('submit', e => this._evtSubmit(e));
|
||||
|
@ -17,8 +17,13 @@ class TagsHeaderView extends events.EventTarget {
|
||||
views.replaceContent(this._hostNode, template(ctx));
|
||||
|
||||
if (this._queryInputNode) {
|
||||
new TagAutoCompleteControl(
|
||||
this._queryInputNode, {transform: misc.escapeSearchTerm});
|
||||
this._autoCompleteControl = new TagAutoCompleteControl(
|
||||
this._queryInputNode,
|
||||
{
|
||||
confirm: tag =>
|
||||
this._autoCompleteControl.replaceSelectedText(
|
||||
misc.escapeSearchTerm(tag.names[0]), true),
|
||||
});
|
||||
}
|
||||
|
||||
search.searchInputNodeFocusHelper(this._queryInputNode);
|
||||
|
Reference in New Issue
Block a user