Open mrooseboom opened 5 years ago
change the setMention function:
function setMention(orgText,mention)
{
var content = $('.trumbowyg-editor').html()
// create content to be inserted
mention = "<mntn>"+mention+"</mntn> "
var regex = new RegExp("" + orgText + "", "g");
content = content.replace(regex, mention)
$('.trumbowyg-editor').html(content)
hideMention()
}
another addition (close the mention list when you click outside):
$(document).mouseup(function(e)
{
var container = $("#mentionItem")
// if the target of the click isn't the container nor a descendant of the container
if (!container.is(e.target) && container.has(e.target).length === 0)
{
hideMention()
}
})
this would be really nice as a plugin (or even nicer integrated in the "official" mention plugin)
/* ===========================================================
* trumbowyg.mention.js v0.2
* Mention plugin for Trumbowyg
* http://alex-d.github.com/Trumbowyg
* ===========================================================
* Author : Rooseboom
* required: php.js https://github.com/hirak/phpjs
*/
(function ($) {
'use strict';
$.extend(true, $.trumbowyg, {
plugins: {
mention: {
init: function (){
$('body').append("<div id=\"mentionItem\" class=\"mentionDiv\"></div>") // add element to show list in
getMentionList() // fill possible mention items array
mention() // watch @ usage while typing
}
}
}
});
})(jQuery);
var mentionList = ""
var activeList = 0
var activeShift = 0
function getMentionList()
{
mentionList = ["Saab","Audi","BMW","Ford"]
// or create ajax function to retrieve array with possible values
}
$(document).mouseup(function(e)
{
var container = $("#mentionItem")
// if the target of the click isn't the container nor a descendant of the container
if (!container.is(e.target) && container.has(e.target).length === 0)
{
hideMention()
}
})
function mention()
{
$(function()
{
$('.trumbowyg-editor').keyup(function()
{
var text = GetPrevWord()
text = text.substring(strpos(text,"@"))
var orgText = text
var revWord = reverseString(text)
//console.log(revWord+" "+orgText)
if(trim(revWord.substring(0,1))== "") // trim is used from php.js
{
hideMention()
}
else if(text.substring(0,1)=="@" && text.length > 1)
{
var revWord = revWord.substring(0,strpos(revWord,"@"))
text = reverseString(revWord)
var replaceText = "@"+text;
text = text.toLowerCase()
var mentionFiltered = mentionList.filter(function (el)
{
el = el.toLowerCase()
return el.indexOf(text) >= 0
})
// close icon is used from icon ion list (add this to your lib or change classes
var showHtml = "<div><i class=\"icon ion-ios-close-outline mentionclose\" onclick=\"hideMention()\"></i>"
var firstupper = 0
mentionFiltered.forEach(function(entry)
{
firstupper = 0
var splitArr = entry.split("|")
var entrypartName = splitArr[0]
var entrypartId = splitArr[1]
var orgEntry = entrypartName+"<mntnid>"+entrypartId+"</mntnid>"
if(entrypartName[0] === entrypartName[0].toUpperCase()){firstupper = 1}
// create underline
var replace = text;
var re = new RegExp(replace,"i")
if(text.length > 0)
{
entrypartName = entrypartName.replace(re, '<u>' + text + '</u>')
}
// redo first char uppercase if that was the case
if(entrypartName.indexOf("<") == 0 && firstupper == 1){
entrypartName = entrypartName.substring(3)
entrypartName = ucfirst(entrypartName)
entrypartName = "<u>"+entrypartName
}
showHtml += "<li class=\"mentionli\" onclick=\"setMention('"+replaceText+"','"+orgEntry+"')\">"+entrypartName+"</li>"
})
showHtml += "</div>"
// where to show to mention box
var coords = getSelectionCoords()
$("#mentionItem").html(showHtml)
$("#mentionItem").show()
if(text.length >= 0)
{
if($("#mentionItem"))
{
$("#mentionItem").css('left', (coords.x - 20) + 'px')
$("#mentionItem").css('top', (coords.y + 20) + 'px')
}
}
}
else
{
hideMention()
}
})
})
}
function setMention(orgText,mention)
{
var content = $('.trumbowyg-editor').html()
// create content to be inserted
mention = "<mntn>"+mention+"</mntn> "
var regex = new RegExp("" + orgText + "", "g");
//console.log(orgText+" > "+mention)
content = content.replace(regex, mention)
$('.trumbowyg-editor').html(content)
hideMention()
}
function hideMention()
{
$("#mentionItem").hide()
}
function GetPrevWord()
{
var text = $('.trumbowyg-editor')
var elementId = text[0].id
var content = text.text()
var element = document.getElementById(elementId)
var caretPos = getCaretCharacterOffsetWithin(element)
var word = ReturnWord(content, caretPos)
if (word != null)
{
return word
}
}
function ReturnWord(text, caretPos)
{
var index = text.indexOf(caretPos);
var preText = text.substring(0, caretPos);
if (preText.indexOf(" ") > 0)
{
var words = preText.split(" ");
return words[words.length - 1]; //return last word
}
else
{
return preText;
}
}
function reverseString(str) {
// Step 1. Use the split() method to return a new array
var splitString = str.split(""); // var splitString = "hello".split("");
// ["h", "e", "l", "l", "o"]
// Step 2. Use the reverse() method to reverse the new created array
var reverseArray = splitString.reverse(); // var reverseArray = ["h", "e", "l", "l", "o"].reverse();
// ["o", "l", "l", "e", "h"]
// Step 3. Use the join() method to join all elements of the array into a string
var joinArray = reverseArray.join(""); // var joinArray = ["o", "l", "l", "e", "h"].join("");
// "olleh"
//Step 4. Return the reversed string
return joinArray; // "olleh"
}
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];
}
if(rect)
{
x = rect.left;
y = rect.top;
}
else
{
x = 0
y = 0
}
}
// Fall back to inserting a temporary element
if (x == 0 && y == 0) {
var span = doc.createElement("span");
if (span.getClientRects) {
// Ensure span has dimensions and position by
// adding a zero-width space character
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);
// Glue any broken text nodes back together
spanParent.normalize();
}
}
}
}
return { x: x, y: y };
}
function getCaretCharacterOffsetWithin(element) {
var caretOffset = 0;
var doc = element.ownerDocument || element.document;
var win = doc.defaultView || doc.parentWindow;
var sel;
if (typeof win.getSelection != "undefined") {
sel = win.getSelection();
if (sel.rangeCount > 0) {
var range = win.getSelection().getRangeAt(0);
var preCaretRange = range.cloneRange();
preCaretRange.selectNodeContents(element);
preCaretRange.setEnd(range.endContainer, range.endOffset);
caretOffset = preCaretRange.toString().length;
}
} else if ( (sel = doc.selection) && sel.type != "Control") {
var textRange = sel.createRange();
var preCaretTextRange = doc.body.createTextRange();
preCaretTextRange.moveToElementText(element);
preCaretTextRange.setEndPoint("EndToEnd", textRange);
caretOffset = preCaretTextRange.text.length;
}
return caretOffset;
}
function getCaretPosition() {
if (window.getSelection && window.getSelection().getRangeAt) {
var range = window.getSelection().getRangeAt(0);
var selectedObj = window.getSelection();
var rangeCount = 0;
var childNodes = selectedObj.anchorNode.parentNode.childNodes;
for (var i = 0; i < childNodes.length; i++) {
if (childNodes[i] == selectedObj.anchorNode) {
break;
}
if (childNodes[i].outerHTML)
rangeCount += childNodes[i].outerHTML.length;
else if (childNodes[i].nodeType == 3) {
rangeCount += childNodes[i].textContent.length;
}
}
return range.startOffset + rangeCount;
}
return -1;
}
Made some improvements
this would be really nice as a plugin (or even nicer integrated in the "official" mention plugin)
It actually is set up as a plugin and I use it that way. It is just not offical ;-)
Could you explain what advantage brings php.js and why it's required? It would be nice to avoid it, since it looks even hard to just download it. Thanks!
it is mentioned in the code: else if(trim(revWord.substring(0,1))== "") // trim is used from php.js
I almost have a working version without php.js, thank you thousands times for providing this. Will post a fully working example as soon as possible.
Let's try to push this as an official plugin. I think the best way would be to extend the official mention plugin. What @Alex-D thinks about that idea?
Please make a PR
I tried hard but I have to say that it ended up being too hard for my abilities :-/ I still think this would make a nice addition but have'nt the needed resources to do it myself.
Would replacing the trim function with this one (so replace trim with PluginTrim) be enough to drop php.js? I have no time to test it and use php.js in my project so I don't need it
function PluginTrim( str, charlist ) {
// Strip whitespace (or other characters) from the beginning and end of a string
//
// + discuss at: http://kevin.vanzonneveld.net/techblog/article/javascript_equivalent_for_phps_trim/
// + version: 804.1712
// + original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + improved by: mdsjack (http://www.mdsjack.bo.it)
// + improved by: Alexander Ermolaev (http://snippets.dzone.com/user/AlexanderErmolaev)
// + input by: Erkekjetter
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + input by: DxGx
// + improved by: Steven Levithan (http://blog.stevenlevithan.com)
// * example 1: trim(' Kevin van Zonneveld ');
// * returns 1: 'Kevin van Zonneveld'
// * example 2: trim('Hello World', 'Hdle');
// * returns 2: 'o Wor'
var whitespace;
if(!charlist){
whitespace = ' \n\r\t\f\x0b\xa0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000';
} else{
whitespace = charlist.replace(/([\[\]\(\)\.\?\/\*\{\}\+\$\^\:])/g, '\$1');
}
for (var i = 0; i < str.length; i++) {
if (whitespace.indexOf(str.charAt(i)) === -1) {
str = str.substring(i);
break;
}
}
for (i = str.length - 1; i >= 0; i--) {
if (whitespace.indexOf(str.charAt(i)) === -1) {
str = str.substring(0, i + 1);
break;
}
}
return whitespace.indexOf(str.charAt(0)) === -1 ? str : '';
}
Why not just the simple and native string.trim()
?
yeah that would be fine as well ;-)
We would love this to become the official or alternatively official plugin! If not possible to add to Trumbowyg official, @mrooseboom - how would you feel about creating a separate repo so we have an authoritative source that we can file issues and contribute PR's - to make this more "official" in our own way.
Hey, any news on this, @mrooseboom ? I would need this so much in my current project!
You can create a Pull Request with the plugin and its documentation if anyone wants. I will be happy to review it :)
This would be great to have. I tried out the revised code above but immediately got the following exception on the first keypress:
trumbowyg.mention-0.2.js:264 Uncaught TypeError: Cannot read properties of null (reading 'ownerDocument')
at getCaretCharacterOffsetWithin (trumbowyg.mention-0.2.js:264:23)
at GetPrevWord (trumbowyg.mention-0.2.js:164:20)
at HTMLDivElement.<anonymous> (trumbowyg.mention-0.2.js:57:24)
at HTMLDivElement.dispatch (jquery-3.1.1.min.js:3:10315)
at q.handle (jquery-3.1.1.min.js:3:8342)
This plugin didn't work for me, I had same problem as @solsticesurfer and i needed it so I reworked it from @mrooseboom. But i had one problem I didn't know how to deal with. So there is one limit and that is that each user can be mentioned only once in every trumbowyg editor.
In file trumbowyg.mention.js
in the top you find all HTML used for this plugin and also variable mentionUserList
where you must save data about users. Format of this data is in the comment above.
To show list of users you have to write at least three characters!
Plugin: mention.zip
Demo with already created ajax requests:
This plugin didn't work for me, I had same problem as @solsticesurfer and i needed it so I reworked it from @mrooseboom. But i had one problem I didn't know how to deal with. So there is one limit and that is that each user can be mentioned only once in every trumbowyg editor.
In file
trumbowyg.mention.js
in the top you find all HTML used for this plugin and also variablementionUserList
where you must save data about users. Format of this data is in the comment above.To show list of users you have to write at least three characters!
Plugin: mention.zip
i found the solution now the mention can start fetching after one typo after @ just replace the folwing code // Get entered text by last at-sign position text = text.substring(lastAtSignCaretPosition, lastAtSignCaretPosition + currentCaretPosition - lastAtSignCaretPosition); if (text.length <= 2) { mentionList.hide(); return; }
replace 2 to
I made another mention plugin for myself since I want it to show while typing and not have a taskbar button for it. My current code (the replace content part is a bit shaky still, working on it):
trumbowyg.mention.js:
and in the ui folder trumbowyg.mention.css: