taiga-front/app/js/medium-mention.js

271 lines
8.5 KiB
JavaScript

var MentionExtension = MediumEditor.Extension.extend({
name: 'mediumMention',
init: function() {
this.subscribe('editableKeyup', this.handleKeyup.bind(this));
this.subscribe('editableKeydown', this.handleKeydown.bind(this));
this.subscribe('blur', this.cancel.bind(this));
},
isEditMode: function() {
return !this.base.origElements.parentNode.classList.contains('read-mode');
},
cancel: function() {
if (this.isEditMode()) {
this.hidePanel();
this.reset();
}
},
handleKeydown: function(e) {
var code = e.keyCode ? e.keyCode : e.which;
if (this.mentionPanel && code === MediumEditor.util.keyCode.ENTER) {
e.preventDefault();
}
},
handleKeyup: function(e) {
var code = e.keyCode ? e.keyCode : e.which;
var isSpace = code === MediumEditor.util.keyCode.SPACE;
var isBackspace = code === MediumEditor.util.keyCode.BACKSPACE;
if (this.mentionPanel) {
this.keyDownMentionPanel(e);
}
var moveKeys = [37, 38, 39, 40];
if (moveKeys.indexOf(code) !== -1) {
return;
}
this.selection = this.document.getSelection();
if (isBackspace && this.selection.focusNode.nodeName.toLowerCase() === 'p') {
return;
}
if (!isSpace && this.selection.rangeCount === 1) {
var endChar = this.selection.getRangeAt(0).startOffset;
var textContent = this.selection.focusNode.textContent;
textContent = textContent.substring(0, endChar);
this.word = this.getLastWord(textContent);
if (this.word.length > 1 && ['@', '#', ':'].indexOf(this.word[0]) != -1) {
this.wrap();
this.showPanel();
MediumEditor.selection.select(
this.document,
this.wordNode.firstChild,
this.word.length
);
return;
}
} else if (isSpace) {
this.cancelMentionSpace();
}
this.hidePanel();
},
reset: function() {
this.wordNode = null;
this.word = null;
this.selection = null;
},
cancelMentionSpace: function() {
if (this.wordNode && this.wordNode.nextSibling) {
var textNode = this.document.createTextNode('');
textNode.textContent = this.word + '\u00A0';
this.wordNode.parentNode.replaceChild(textNode, this.wordNode);
MediumEditor.selection.select(this.document, textNode, this.word.length + 1);
}
this.reset();
},
wrap: function() {
var range = this.selection.getRangeAt(0).cloneRange();
if (range.startContainer.parentNode.nodeName.toLowerCase() === 'a') {
var parentLink = range.startContainer.parentNode.parentNode;
var textNode = this.document.createTextNode(range.startContainer.parentNode.innerText);
parentLink.replaceChild(textNode, range.startContainer.parentNode);
this.selection.removeAllRanges();
range = document.createRange();
range.setStart(textNode, textNode.length);
range.setEnd(textNode, textNode.length);
this.selection.addRange(range);
}
if (!range.startContainer.parentNode.classList.contains('mention')) {
this.wordNode = this.document.createElement('span');
this.wordNode.classList.add('mention');
range.setStart(range.startContainer, this.selection.getRangeAt(0).startOffset - this.word.length);
range.surroundContents(this.wordNode);
this.selection.removeAllRanges();
this.selection.addRange(range);
//move cursor to old position
range.setStart(range.startContainer, range.endOffset);
range.setStart(range.endContainer, range.endOffset);
this.selection.removeAllRanges();
this.selection.addRange(range);
} else {
this.wordNode = range.startContainer.parentNode;
}
},
refreshPositionPanel: function() {
var bound = this.wordNode.getBoundingClientRect();
this.mentionPanel.style.top = this.window.pageYOffset + bound.bottom + 'px';
this.mentionPanel.style.left = this.window.pageXOffset + bound.left + 'px';
},
selectMention: function(item) {
if (item.image) {
var img = document.createElement('img');
img.src = item.image;
this.wordNode.parentNode.replaceChild(img, this.wordNode);
this.wordNode = img;
} else {
var link = document.createElement('a');
link.setAttribute('href', item.url);
if (item.ref) {
link.innerText = '#' + item.ref + '-' + item.subject;
} else {
link.innerText = '@' + item.username;
}
this.wordNode.parentNode.replaceChild(link, this.wordNode);
this.wordNode = link;
}
var textNode = this.document.createTextNode('');
textNode.textContent = '\u00A0';
this.wordNode.parentNode.insertBefore(textNode, this.wordNode.nextSibling);
MediumEditor.selection.select(this.document, textNode, 1);
var target = this.base.getFocusedElement();
this.base.events.updateInput(target, {
target: target,
currentTarget: target
});
this.hidePanel();
this.reset();
},
showPanel: function() {
if(document.querySelectorAll('.medium-editor-mention-panel').length) {
this.refreshPositionPanel();
this.getItems(this.word, this.renderPanel.bind(this));
return;
}
var el = this.document.createElement('div');
el.classList.add('medium-editor-mention-panel');
this.mentionPanel = el;
this.getEditorOption('elementsContainer').appendChild(el);
this.refreshPositionPanel();
this.getItems(this.word, this.renderPanel.bind(this));
},
keyDownMentionPanel: function(e) {
var code = e.keyCode ? e.keyCode : e.which;
var active = this.mentionPanel.querySelector('.active');
this.wordNode = document.querySelector('span.mention');
if(!active) {
return;
}
if (code === MediumEditor.util.keyCode.ENTER) {
e.preventDefault();
e.stopPropagation();
var event = document.createEvent('HTMLEvents');
event.initEvent('mousedown', true, false);
active.dispatchEvent(event);
return;
}
active.classList.remove('active');
if (code === 38) {
if(active.previousSibling) {
active.previousSibling.classList.add('active');
} else {
active.parentNode.lastChild.classList.add('active');
}
} else if (code === 40) {
if(active.nextSibling) {
active.nextSibling.classList.add('active');
} else {
active.parentNode.firstChild.classList.add('active');
}
}
},
renderPanel: function(items) {
this.mentionPanel.innerHTML = '';
if (!items.length) return;
var ul = this.document.createElement('ul');
ul.classList.add('medium-mention');
items.forEach(function(it) {
var li = this.document.createElement('li');
if (it.image) {
var img = this.document.createElement('img');
img.src = it.image;
li.appendChild(img);
var textNode = document.createTextNode('');
textNode.textContent = ' ' + it.name;
li.appendChild(textNode);
} else if (it.ref) {
li.innerText = '#' + it.ref + ' - ' + it.subject;
} else {
li.innerText = '@' + it.username;
}
li.addEventListener('mousedown', this.selectMention.bind(this, it));
ul.appendChild(li);
}.bind(this));
ul.firstChild.classList.add('active');
this.mentionPanel.appendChild(ul);
},
hidePanel: function() {
if (this.mentionPanel) {
this.mentionPanel.parentNode.removeChild(this.mentionPanel);
this.mentionPanel = null;
}
},
getLastWord: function(text) {
var n = text.split(' ');
return n[n.length - 1].trim();
}
});