我在项目中使用此插件来标记/提及人物。我想再添加一个选项来标记项目中的项目。这里用于标记人们' @'用来。我想用'#'也用于标记。哪个部分我必须添加或修改标记人和列表在我的project.i想标记人们用' @'并希望用'#'标记列表。
var tmpEle = null;
(function ($) {
var KEY = {
AT: 64,
BACKSPACE: 8,
DELETE: 46,
TAB: 9,
ESC: 27,
RETURN: 13,
LEFT: 37,
UP: 38,
RIGHT: 39,
DOWN: 40,
SPACE: 32,
HOME: 36,
END: 35,
COMMA: 188,
NUMPAD_ADD: 107,
NUMPAD_DECIMAL: 110,
NUMPAD_DIVIDE: 111,
NUMPAD_ENTER: 108,
NUMPAD_MULTIPLY: 106,
NUMPAD_SUBTRACT: 109,
PAGE_DOWN: 34,
PAGE_UP: 33,
PERIOD: 190,
};
jQuery.fn.mentiony = function (method, options) {
var defaults = {
debug: 0, // Set 1 to see console log message of this plugin
applyInitialSize: true,
globalTimeout: null, // Don't overwrite this config
timeOut: 400, // Do mention only when user input idle time > this value
triggerChar: '@', // @keyword-to-mention
onDataRequest: function (mode, keyword, onDataRequestCompleteCallback) {
},
onKeyPress: function (event, oldInputEle, newEditableEle) {
oldInputEle.trigger(event);
},
onKeyUp: function (event, oldInputEle, newEditableEle) {
oldInputEle.trigger(event);
},
onBlur: function (event, oldInputEle, newEditableEle) {
oldInputEle.trigger(event);
},
onPaste: function (event, oldInputEle, newEditableEle) {
oldInputEle.trigger(event);
},
onInput: function (oldInputEle, newEditableEle) {
},
// adjust popover relative position with its parent.
popoverOffset: {
x: -30,
y: 0
},
templates: {
container: '<div id="mentiony-container-[ID]" class="mentiony-container"></div>',
content: '<div id="mentiony-content-[ID]" class="mentiony-content" contenteditable="true"></div>',
popover: '<div id="mentiony-popover-[ID]" class="mentiony-popover"></div>',
list: '<ul id="mentiony-popover-[ID]" class="mentiony-list"></ul>',
listItem: '<li class="mentiony-item" data-item-id="">' +
'<div class="row">' +
'<div class="col-xs-3 col-sm-3 col-md-3 col-lg-3">' +
'<img src="https://avatars2.githubusercontent.com/u/1859127?v=3&s=140">' +
'</div>' +
'<div class="pl0 col-xs-9 col-sm-9 col-md-9 col-lg-9">' +
'<p class="title">Company name</p>' +
'<p class="help-block">Addition information</p>' +
'</div>' +
'</div>' +
'</li>',
normalText: '<span class="normal-text"> </span>',
highlight: '<span class="highlight"></span>',
highlightContent: '<a href="[HREF]" data-item-id="[ITEM_ID]" class="mentiony-link">[TEXT]</a>',
}
};
if (typeof method === 'object' || !method) {
options = method;
}
var settings = $.extend({}, defaults, options);
return this.each(function () {
var instance = $.data(this, 'mentiony') || $.data(this, 'mentiony', new MentionsInput(settings));
if (typeof instance[method] === 'function') {
return instance[method].apply(this, Array.prototype.slice.call(outerArguments, 1));
} else if (typeof method === 'object' || !method) {
return instance.init.call(this, this);
} else {
$.error('Method ' + method + ' does not exist');
}
});
};
var MentionsInput = function (settings) {
var elmInputBoxContainer, elmInputBoxContent, elmInputBox,
elmInputBoxInitialWidth, elmInputBoxInitialHeight,
editableContentLineHeightPx,
popoverEle, list, elmInputBoxId,
elmInputBoxContentAbsPosition = {top: 0, left: 0},
dropDownShowing = false,
events = {
keyDown: false,
keyPress: false,
input: false,
keyup: false,
},
currentMention = {
keyword: '',
jqueryDomNode: null, // represent jQuery dom data
mentionItemDataSet: [], // list item json data was store here
lastActiveNode: 0,
charAtFound: false, // tracking @ char appear or not
}
;
var needMention = false; // Mention state
var inputId = Math.random().toString(36).substr(2, 6); // generate 6 character rand string
var onDataRequestCompleteCallback = function (responseData) {
populateDropdown(currentMention.keyword, responseData);
};
function initTextArea(ele) {
elmInputBox = $(ele);
if (elmInputBox.attr('data-mentions-input') == 'true') {
return;
} else {
elmInputBox.attr('data-mentions-input', 'true');
if (elmInputBox.attr('id').length == 0) {
elmInputBoxId = 'mentiony-input-' + inputId;
elmInputBox.attr('id', elmInputBoxId);
} else {
elmInputBoxId = elmInputBox.attr('id');
}
}
// Initial UI information
elmInputBoxInitialWidth = elmInputBox.prop('scrollWidth');
elmInputBoxInitialHeight = elmInputBox.prop('scrollHeight');
// Container
elmInputBoxContainer = $(settings.templates.container.replace('[ID]', inputId));
elmInputBoxContent = $(settings.templates.content.replace('[ID]', inputId));
// Make UI and hide the textarea
var placeholderText = elmInputBox.attr('placeholder');
if (typeof placeholderText === 'undefined') {
placeholderText = elmInputBox.text();
}
elmInputBoxContent.attr('data-placeholder', placeholderText);
elmInputBoxContainer.append(elmInputBox.clone().addClass('mention-input-hidden'));
elmInputBoxContainer.append(elmInputBoxContent);
elmInputBox.replaceWith(elmInputBoxContainer);
popoverEle = $(settings.templates.popover.replace('[ID]', inputId));
list = $(settings.templates.list.replace('[ID]', inputId));
elmInputBoxContainer.append(popoverEle);
popoverEle.append(list);
// Reset the input
elmInputBox = $('#' + elmInputBoxId);
// Update initial UI
var containerPadding = parseInt(elmInputBoxContainer.css('padding'));
if (settings.applyInitialSize) {
elmInputBoxContainer.addClass('initial-size');
elmInputBoxContainer.css({width: (elmInputBoxInitialWidth) + 'px'});
elmInputBoxContent.width((elmInputBoxInitialWidth - 2 * containerPadding) + 'px');
}
else {
elmInputBoxContainer.addClass('auto-size');
}
elmInputBoxContent.css({minHeight: elmInputBoxInitialHeight + 'px'});
elmInputBoxContentAbsPosition = elmInputBoxContent.offset();
editableContentLineHeightPx = parseInt($(elmInputBoxContent.css('line-height')).selector);
elmInputBoxContent.bind('keydown', onInputBoxKeyDown);
elmInputBoxContent.bind('keypress', onInputBoxKeyPress);
elmInputBoxContent.bind('input', onInputBoxInput);
elmInputBoxContent.bind('keyup', onInputBoxKeyUp);
elmInputBoxContent.bind('click', onInputBoxClick);
elmInputBoxContent.bind('blur', onInputBoxBlur);
elmInputBoxContent.bind('paste', onInputBoxPaste);
}
function onInputBoxKeyDown(e) {
events = {
keyDown: true,
keyPress: false,
input: false,
keyup: false,
};
if (dropDownShowing) {
return handleUserChooseOption(e);
}
}
function onInputBoxKeyPress(e) {
// log('onInputBoxKeyPress');
events.keyPress = true;
if (!needMention) {
// Try to check if need mention
needMention = (e.keyCode === KEY.AT);
// log(needMention, 'needMention', 'info');
}
settings.onKeyPress.call(this, e, elmInputBox, elmInputBoxContent);
}
function onInputBoxInput() {
// log('onInputBoxInput');
events.input = true;
settings.onInput.call(this, elmInputBox, elmInputBoxContent);
}
/**
* Put all special key handle here
* @param e
*/
function onInputBoxKeyUp(e) {
// log('onInputBoxKeyUp');
events.keyup = true;
// log(events, 'events');
if (events.input) {
updateDataInputData(e);
}
if (needMention) {
// Update mention keyword only inputing(not enter), left, right
if (e.keyCode !== KEY.RETURN && (events.input || e.keyCode === KEY.LEFT || e.keyCode === KEY.RIGHT)) {
updateMentionKeyword(e);
doSearchAndShow();
}
}
settings.onKeyUp.call(this, e, elmInputBox, elmInputBoxContent);
}
function onInputBoxClick(e) {
// log('onInputBoxClick');
if (needMention) {
updateMentionKeyword(e);
doSearchAndShow();
}
}
function onInputBoxBlur(e) {
// log('onInputBoxBlur');
settings.onBlur.call(this, e, elmInputBox, elmInputBoxContent);
}
function onInputBoxPaste(e) {
// log('onInputBoxPaste');
settings.onPaste.call(this, e, elmInputBox, elmInputBoxContent);
}
function onListItemClick(e) {
//$(this) is the clicked listItem
setSelectedMention($(this));
choseMentionOptions(true);
}
function updateDataInputData(e) {
var elmInputBoxText = elmInputBoxContent.html();
elmInputBox.val(convertSpace(elmInputBoxText));
log(elmInputBox.val(), 'elmInputBoxText : ');
tmpEle = elmInputBox;
}
function trimSpace(text) {
return text.replace(/^( | |\s)+|( | |\s)+$/g, '');
}
function convertSpace(text) {
return text.replace(/( )+/g, ' ');
}
/**
* Handle User search action with timeout, and show mention if needed
*/
function doSearchAndShow() {
if (settings.timeOut > 0) {
if (settings.globalTimeout !== null) {
clearTimeout(settings.globalTimeout);
}
settings.globalTimeout = setTimeout(function () {
settings.globalTimeout = null;
settings.onDataRequest.call(this, 'search', currentMention.keyword, onDataRequestCompleteCallback);
}, settings.timeOut);
} else {
settings.onDataRequest.call(this, 'search', currentMention.keyword, onDataRequestCompleteCallback);
}
}
function populateDropdown(keyword, responseData) {
list.empty();
currentMention.jqueryDomNode = null;
currentMention.mentionItemDataSet = responseData;
if (responseData.length) {
if (currentMention.charAtFound === true) {
showDropDown();
}
responseData.forEach(function (item, index) {
var listItem = $(settings.templates.listItem);
listItem.attr('data-item-id', item.id);
listItem.find('img:first').attr('src', item.avatar);
listItem.find('p.title:first').html(item.name);
listItem.find('p.help-block:first').html(item.info);
listItem.bind('click', onListItemClick);
list.append(listItem);
});
} else {
hideDropDown();
}
}
function showDropDown() {
var curAbsPos = getSelectionCoords();
dropDownShowing = true;
popoverEle.css({
display: 'block',
top: curAbsPos.y - (elmInputBoxContentAbsPosition.top - $(document).scrollTop()) + (editableContentLineHeightPx + 10),
left: curAbsPos.x - elmInputBoxContentAbsPosition.left
});
}
function hideDropDown() {
dropDownShowing = false;
popoverEle.css({display: 'none'});
}
function handleUserChooseOption(e) {
if (!dropDownShowing) {
return true;
}
if (e.keyCode === KEY.UP || e.keyCode === KEY.DOWN) {
choosingMentionOptions(e);
return false;
}
// Try to exit mention state: Stop mention if @, Home, Enter, Tabs
if ((e.keyCode === KEY.HOME)
|| (e.keyCode === KEY.RETURN)
|| (e.keyCode === KEY.TAB)
) {
choseMentionOptions();
return false;
}
return true;
}
function updateMentionKeyword(e) {
if (document.selection) {
// var node = document.selection.createRange().parentElement(); // IE
var node = document.selection.createRange(); // IE
// TODO: Test on IE
} else {
// var node = window.getSelection().anchorNode.parentNode; // everyone else
var node = window.getSelection().anchorNode; // everyone else
}
var textNodeData = node.data;
if (typeof textNodeData === 'undefined') {
textNodeData = '';
}
var cursorPosition = getSelectionEndPositionInCurrentLine(); // passing the js DOM ele
currentMention.lastActiveNode = node;
currentMention.keyword = '';
var i = cursorPosition - 1; // NOTE: cursorPosition is Non-zero base
var next = true;
while (next) {
var charAt = textNodeData.charAt(i);
if (charAt === '' || charAt === settings.triggerChar) {
next = false;
}
i--;
}
currentMention.keyword = textNodeData.substring(i + 1, cursorPosition);
if (currentMention.keyword.indexOf(settings.triggerChar) === -1) {
currentMention.keyword = '';
currentMention.charAtFound = false;
// NOTE: Still need mention but turn off dropdown now
hideDropDown();
} else {
currentMention.keyword = currentMention.keyword.substring(1, cursorPosition);
currentMention.charAtFound = true;
}
log(currentMention.keyword, 'currentMention.keyword');
}
function getMentionKeyword() {
return currentMention.keyword;
}
function setSelectedMention(item) {
currentMention.jqueryDomNode = item;
updateSelectedMentionUI(item);
log(item, 'setSelectedMention item: ');
}
function updateSelectedMentionUI(selectedMentionItem) {
$.each(list.children(), function (i, listItem) {
$(listItem).removeClass('active');
});
selectedMentionItem.addClass('active');
}
function choosingMentionOptions(e) {
log('choosingMentionOptions');
// Get Selected mention Item
if (currentMention.jqueryDomNode === null) {
setSelectedMention(list.children().first());
}
var item = [];
if (e.keyCode === KEY.DOWN) {
item = currentMention.jqueryDomNode.next();
} else if (e.keyCode === KEY.UP) {
item = currentMention.jqueryDomNode.prev();
}
if (item.length === 0) {
item = currentMention.jqueryDomNode;
}
setSelectedMention(item);
}
function choseMentionOptions(chooseByMouse) {
if (chooseByMouse === 'undefined') {
chooseByMouse = false;
}
log('choosedMentionOptions by ' + (chooseByMouse ? 'Mouse' : 'Keyboard'));
var currentMentionItemData = {};
var selectedId = currentMention.jqueryDomNode.attr('data-item-id');
for (var i = 0, len = currentMention.mentionItemDataSet.length; i < len; i++) {
if (selectedId == currentMention.mentionItemDataSet[i].id) {
currentMentionItemData = currentMention.mentionItemDataSet[i];
break;
}
}
var highlightNode = $(settings.templates.highlight);
var highlightContentNode = $(settings.templates.highlightContent
.replace('[HREF]', currentMentionItemData.href)
.replace('[TEXT]', currentMentionItemData.name)
.replace('[ITEM_ID]', currentMentionItemData.id)
);
highlightNode.append(highlightContentNode);
replaceTextInRange('@' + currentMention.keyword, highlightNode.prop('outerHTML'), chooseByMouse);
// Finish mention
log('Finish mention', '', 'warn');
needMention = false; // Reset mention state
currentMention.keyword = ''; // reset current Data if start with @
hideDropDown();
updateDataInputData();
}
function log(msg, prefix, level) {
if (typeof level === 'undefined') {
level = 'log';
}
if (settings.debug === 1) {
eval("console." + level + "(inputId, prefix ? prefix + ':' : '', msg);");
}
}
function replaceTextInRange(fromString, toTextHtml, choosedByMouse) {
var positionInfo = {
startBefore: 0,
startAfter: 0,
stopBefore: 0,
stopAfter: 0,
};
var sel = window.getSelection();
var range;
if (choosedByMouse !== 'undefined' && choosedByMouse === true) {
var lastActiveNode = currentMention.lastActiveNode;
try {
var offset = lastActiveNode.data.length;
} catch (e) {
log(e, 'lastActiveNode Error:');
var offset = 0;
}
range = document.createRange();
range.setStart(lastActiveNode, offset);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
}
var isIE = false;
var stopPos = sel.focusOffset;
var startPos = stopPos - fromString.length;
if (window.getSelection) {
isIE = false;
sel = window.getSelection();
if (sel.rangeCount > 0) {
range = sel.getRangeAt(0).cloneRange();
range.collapse(true);
}
} else if ((sel = document.selection) && sel.type != "Control") {
range = sel.createRange();
isIE = true;
}
if (startPos !== stopPos) {
// replace / Remove content
range.setStart(sel.anchorNode, startPos);
range.setEnd(sel.anchorNode, stopPos);
range.deleteContents();
}
// insert
var node = document.createElement('span');
node.setAttribute('class', 'mention-area');
node.innerHTML = toTextHtml;
range.insertNode(node);
range.setEnd(sel.focusNode, range.endContainer.length);
positionInfo.startBefore = startPos;
positionInfo.stopBefore = stopPos;
positionInfo.startAfter = startPos;
positionInfo.stopAfter = startPos + node.innerText.length;
// move cursor to end of keyword after replace
var stop = false;
node = $(sel.anchorNode);
while (!stop) {
if (node.next().text().length === 0) {
stop = true;
}
else {
node = node.next();
}
}
// insert <newElem> after list
var newElem = $(settings.templates.normalText).insertAfter(node);
// move caret to after <newElem>
range = document.createRange();
range.setStartAfter(newElem.get(0));
range.setEndAfter(newElem.get(0));
sel.removeAllRanges();
sel.addRange(range);
return positionInfo;
}
// Public methods
return {
init: function (domTarget) {
initTextArea(domTarget);
},
};
};
function getSelectionCoords(win) {
win = win || window;
var doc = win.document;
var sel = doc.selection, range, rects, rect;
var x = 0, y = 0;
if (sel) {
if (sel.type != "Control") {
range = sel.createRange();
range.collapse(true);
x = range.boundingLeft;
y = range.boundingTop;
}
} else if (win.getSelection) {
sel = win.getSelection();
if (sel.rangeCount) {
range = sel.getRangeAt(0).cloneRange();
if (range.getClientRects) {
range.collapse(true);
rects = range.getClientRects();
if (rects.length > 0) {
rect = rects[0];
}
x = rect.left;
y = rect.top;
}
if (x == 0 && y == 0) {
var span = doc.createElement("span");
if (span.getClientRects) {
span.appendChild(doc.createTextNode("\u200b"));
range.insertNode(span);
rect = span.getClientRects()[0];
x = rect.left;
y = rect.top;
var spanParent = span.parentNode;
spanParent.removeChild(span);
spanParent.normalize();
}
}
}
}
return {x: x, y: y};
}
function getSelectionEndPositionInCurrentLine() {
var selectionEndPos = 0;
if (window.getSelection) {
var sel = window.getSelection();
selectionEndPos = sel.focusOffset;
}
return selectionEndPos;
}
}(jQuery));
<textarea id="t3" name="mention" class="mention" rows="6">Try to @John</textarea>
$('textarea').mentiony({ //#user_list
onDataRequestUser: function (mode, keyword, onDataRequestCompleteCallbackUser) {
$.ajax({
url: "xyz.php",
method: "post",
data: {keyword: keyword},
dataType: "json",
success: function (response) {
var data = response;
console.log(data);
data = jQuery.grep(data, function( item ) {
return item.name.toLowerCase().indexOf(keyword.toLowerCase()) > -1;
});
onDataRequestCompleteCallbackUser.call(this, data);
}
});
},
timeOut: 500, // Timeout to show mention after press @//#user_list
});