271 lines
8.5 KiB
JavaScript
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();
|
|
}
|
|
});
|